commit 2cf23a2bd145f31dc98fbda8cd004ca93c2bbd66 Author: Cooper Ransom Date: Mon Feb 26 10:54:56 2024 -0500 Add sudo-flix code from github diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..bb73799b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +node_modules +build +.env.local +.github +.vscode diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..6c2eeb87 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_size = 2 +indent_style = space diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..e9b54595 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,108 @@ +const a11yOff = Object.keys(require("eslint-plugin-jsx-a11y").rules).reduce( + (acc, rule) => { + acc[`jsx-a11y/${rule}`] = "off"; + return acc; + }, + {} +); + +module.exports = { + env: { + browser: true + }, + extends: [ + "airbnb", + "airbnb/hooks", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + ignorePatterns: [ + "public/*", + "dist/*", + "/*.js", + "/*.ts", + "/*.mts", + "/plugins/*.ts", + "/plugins/*.mjs", + "/themes/**/*.ts" + ], + parser: "@typescript-eslint/parser", + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: "./" + }, + settings: { + "import/resolver": { + typescript: { + project: "./tsconfig.json" + } + } + }, + plugins: ["@typescript-eslint", "import", "prettier"], + rules: { + "react/jsx-uses-react": "off", + "react/react-in-jsx-scope": "off", + "react/require-default-props": "off", + "react/destructuring-assignment": "off", + "no-underscore-dangle": "off", + "@typescript-eslint/no-explicit-any": "off", + "no-console": ["warn", { allow: ["warn", "error", "debug", "info"] }], + "@typescript-eslint/no-this-alias": "off", + "import/prefer-default-export": "off", + "@typescript-eslint/no-empty-function": "off", + "no-shadow": "off", + "@typescript-eslint/no-shadow": ["error"], + "no-restricted-syntax": "off", + "import/no-unresolved": ["error", { ignore: ["^virtual:"] }], + "react/jsx-props-no-spreading": "off", + "consistent-return": "off", + "no-continue": "off", + "no-eval": "off", + "no-await-in-loop": "off", + "no-nested-ternary": "off", + "prefer-destructuring": "off", + "no-param-reassign": "off", + "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], + "react/jsx-filename-extension": [ + "error", + { extensions: [".js", ".tsx", ".jsx"] } + ], + "import/extensions": [ + "error", + "ignorePackages", + { + ts: "never", + tsx: "never" + } + ], + "import/order": [ + "error", + { + groups: [ + "builtin", + "external", + "internal", + ["sibling", "parent"], + "index", + "unknown" + ], + "newlines-between": "always", + alphabetize: { + order: "asc", + caseInsensitive: true + } + } + ], + "sort-imports": [ + "error", + { + ignoreCase: false, + ignoreDeclarationSort: true, + ignoreMemberSort: false, + memberSyntaxSortOrder: ["none", "all", "multiple", "single"], + allowSeparatedGroups: true + } + ], + ...a11yOff + } +}; diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..6313b56c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..74587723 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @movie-web/project-leads diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..412c4af2 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +codeofconduct@movie-web.app. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..926899a8 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,94 @@ +# Contributing Guidelines for movie-web + +Thank you for investing your time in contributing to our project! Your contribution will be reflected on [movie-web.app](https://movie-web.app). + +Please read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. + +## Contents + - [New Contributor Guide](#new-contributor-guide) + - [Requesting a feature or reporting a bug](#requesting-a-feature-or-reporting-a-bug) + - [Discord Server](#discord-server) + - [GitHub Issues](#github-issues) + - [Before you start](#before-you-start) + - [Contributing](#before-you-start) + - [Recommended Development Environment](#recommended-development-environment) + - [Tips](#tips) + - [Language Contributions](#language-contributions) + +## New contributor guide + +To get an overview of the project, read the [README](README.md). Here are some resources to help you get started with open-source contributions: + +- [Finding ways to contribute to open-source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) +- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git) +- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) + + +## Requesting a feature or reporting a bug +There are two places where to request features or report bugs: + - GitHub Issues + - The movie-web Discord server + +### Discord Server +If you do not have a GitHub account or want to discuss a feature or bug with us before making an issue, you can join our Discord server. + +Discord Server + +### GitHub Issues +To make a GitHub issue for movie-web, please visit the [new issue page](https://github.com/movie-web/movie-web/issues/new/choose) where you can pick either the "Bug Report" or "Feature Request" template. + +When filling out an issue template, please include as much detail as possible and any screenshots or console logs as appropriate. + +After an issue is created, it will be assigned either the https://github.com/movie-web/movie-web/labels/bug or https://github.com/movie-web/movie-web/labels/feature label, along with https://github.com/movie-web/movie-web/labels/awaiting-approval. One of our maintainers will review your issue and, if it's accepted, will set the https://github.com/movie-web/movie-web/labels/approved label. + +## Before you start! +Before starting a contribution, please check your contribution is part of an open issue on [our issues page](https://github.com/movie-web/movie-web/issues?q=is%3Aopen+is%3Aissue+label%3Aapproved). + +GitHub issues are how we track our bugs and feature requests that will be implemented into movie-web - all contributions **must** have an issue and be approved by a maintainer before a pull request can be worked on. + +If a pull request is opened before an issue is created and accepted, you may risk having your pull request rejected! Always check with us before starting work on a feature - we don't want to waste your time! + +> **Note** +> The exception to this are language contributions, which are discussed in [this section](#language-contributions) + +Also, make sure that the issue you would like to work on has been given the https://github.com/movie-web/movie-web/labels/approved label by a maintainer. Otherwise, if we reject the issue, it means your work will have gone to waste! + +## Contributing +If you're here because you'd like to work on an issue, amazing! Thank you for even considering contributing to movie-web; it means a lot :heart: + +Firstly, make sure you've read the [Before you start!](#before-you-start) section! + +When you have found a GitHub issue you would like to work on, you can request to be assigned to the issue by commenting on the GitHub issue. + +If you are assigned to an issue but can't complete it for whatever reason, no problem! Just let us know, and we will open up the issue to have someone else assigned. + +### Recommended Development Environment +Our recommended development environment to work on movie-web is: +- [Visual Studio Code](https://code.visualstudio.com/) +- [ESLint Extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) +- [EditorConfig Extension](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) + +When opening Visual Studio Code, you will be prompted to install our recommended extensions if they are not installed for you. + +Our project is set up to enforce formatting and code style standards using ESLint. + +### Tips +Here are some tips to make sure that your pull requests are :pinched_fingers: first time: + +- KISS - Keep It Simple Soldier! - Simple code makes readable and efficient code! +- Follow standard best practices for TypeScript and React. +- Keep as much as possible to the style of movie-web. Look around our codebase to familiarise yourself with how we do things! +- Ensure to take note of the ESLint errors and warnings! **Do not ignore them!** They are there for a reason. +- Test, test, test! Make sure you thoroughly test the features you are contributing. + +### Language Contributions +Language contributions help movie-web massively, allowing people worldwide to use our app! + +We use weblate for crowdsourcing our translations. [Click here to go to our translation tool.](https://weblate.movie-web.app/projects/movie-web/website/) + +1. First make sure you make an account. (click the link above) +2. Click the language you want to help translate, if it's not listed you can click the plus top left to add a new language. +3. In the top right of the screen, click "translate" +4. Here you will be prompted a key to translate, fill in a translation and proceed to the next item by pressing "save and continue". +5. Thats all there is to it, every translation will eventually come through and be pushed with an update. This usually doesn't take longer than a week. diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 00000000..3bf7111e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,50 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["bug", "awaiting-approval"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Please fill out with as much detail as possible + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! + validations: + required: true + - type: dropdown + id: browsers + attributes: + label: What browsers are you seeing the problem on? + multiple: true + options: + - Firefox + - Chrome + - Safari + - Microsoft Edge + - Other (tell us in input box below) + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce? + description: What steps have you taken to see the bug? (OPTIONAL) + placeholder: 1. ... + validations: + required: false + - type: textarea + id: other-info + attributes: + label: Other relevant information + description: | + Feel free to give us any more information that doesn't fit the above text boxes. + + Tip: You can attach files by clicking this textbox and dragging in files + validations: + required: false + diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 00000000..9d5bb8be --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,26 @@ +name: Feature Request +description: Suggest a new feature +title: "[Feature]: " +labels: ["feature", "awaiting-approval"] +assignees: [] +body: + - type: textarea + id: what-feature + attributes: + label: What feature do you want to add? + placeholder: A new button! + validations: + required: true + - type: textarea + id: why-feature + attributes: + label: Why do you want to have this feature? + placeholder: A new button! + validations: + required: true + - type: textarea + id: other-details + attributes: + label: Any other details to share? + validations: + required: false diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..83bf16ae --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +The movie-web maintainers only support the latest version of movie-web published at https://movie-web.app. + +Support is not provided for any forks or mirrors of movie-web. + +## Reporting a Vulnerability + +There are two ways you can contact the movie-web maintainers to report a vulnerability: + - Email [security@movie-web.app](mailto:security@movie-web.app) + - Report the vulnerability in the [movie-web Discord server](https://discord.movie-web.app) diff --git a/.github/logo-dark.svg b/.github/logo-dark.svg new file mode 100644 index 00000000..e0258f10 --- /dev/null +++ b/.github/logo-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/.github/logo-light.svg b/.github/logo-light.svg new file mode 100644 index 00000000..dc5a242f --- /dev/null +++ b/.github/logo-light.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..c072b7f5 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,6 @@ +This pull request resolves #XXX + + - [ ] I have read and agreed to the [code of conduct](https://github.com/movie-web/movie-web/blob/dev/.github/CODE_OF_CONDUCT.md). + - [ ] I have read and complied with the [contributing guidelines](https://github.com/movie-web/movie-web/blob/dev/.github/CONTRIBUTING.md). + - [ ] What I'm implementing was assigned to me and is an [approved issue](https://github.com/movie-web/movie-web/issues?q=is%3Aopen+is%3Aissue+label%3Aapproved). For reference, please take a look at our [GitHub projects](https://github.com/movie-web/movie-web/projects). + - [ ] I have tested all of my changes. diff --git a/.github/workflows/deploying.yml b/.github/workflows/deploying.yml new file mode 100644 index 00000000..e07a60a5 --- /dev/null +++ b/.github/workflows/deploying.yml @@ -0,0 +1,181 @@ +name: Deploying + +on: + push: + branches: + - master + +jobs: + build_pwa: + name: Build PWA + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install pnpm packages + run: pnpm install + + - name: Build project + run: pnpm run build:pwa + + - name: Upload production-ready build files + uses: actions/upload-artifact@v4 + with: + name: pwa + path: ./dist + + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install pnpm packages + run: pnpm install + + - name: Build project + run: pnpm run build + + - name: Upload production-ready build files + uses: actions/upload-artifact@v4 + with: + name: normal + path: ./dist + + release: + name: Release + needs: [build, build_pwa] + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download PWA artifact + uses: actions/download-artifact@v4 + with: + name: pwa + path: ./dist_pwa + + - name: Zip PWA files + run: cd dist_pwa && zip -r ../movie-web.pwa.zip . + + - name: Download normal artifact + uses: actions/download-artifact@v4 + with: + name: normal + path: ./dist_normal + + - name: Zip normal files + run: cd dist_normal && zip -r ../movie-web.zip . + + - name: Get version + id: package-version + uses: martinbeentjes/npm-get-version-action@main + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.package-version.outputs.current-version }} + release_name: Movie web v${{ steps.package-version.outputs.current-version }} + draft: false + prerelease: false + + - name: Upload release (PWA) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./movie-web.pwa.zip + asset_name: movie-web.pwa.zip + asset_content_type: application/zip + + - name: Upload Release (Normal) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./movie-web.zip + asset_name: movie-web.zip + asset_content_type: application/zip + + registry: + name: Push to registry + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + - name: Get version + id: package-version + uses: martinbeentjes/npm-get-version-action@main + + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + latest=auto + tags: | + type=semver,pattern={{version}},value=v${{ steps.package-version.outputs.current-version }} + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v5 + with: + push: true + platforms: linux/amd64,linux/arm64 + context: . + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta.outputs.tags }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/linting_testing.yml b/.github/workflows/linting_testing.yml new file mode 100644 index 00000000..c1d5f833 --- /dev/null +++ b/.github/workflows/linting_testing.yml @@ -0,0 +1,74 @@ +name: Linting and Testing + +on: + push: + branches: + - master + - dev + pull_request: + +jobs: + linting: + name: Run Linters + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install pnpm packages + run: pnpm install + + - name: Run ESLint + run: pnpm run lint + + building: + name: Build project + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install pnpm packages + run: pnpm install + + - name: Build Project + run: pnpm run build + + docker: + name: Build Docker + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + push: false + context: . diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..92594324 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/dist +dev-dist +/stats.html + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* + +# other package managers +yarn.lock +package-lock.json + +# config +.env diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..bf2e7648 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +shamefully-hoist=true diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..118c0efb --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "editorconfig.editorconfig" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..741f0c0a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "eslint.format.enable": true, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint" + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..cc9b84be --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM node:20-alpine as build +WORKDIR /app +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable + +COPY package.json ./ +COPY pnpm-lock.yaml ./ +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY . ./ +RUN pnpm run build + +# production environment +FROM nginx:stable-alpine +COPY --from=build /app/dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..d71bce99 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 James Hawkins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..7f4662a2 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ + +

+

+

+ +
+ 🔵 discord 🟢 website +

+

+ +# ⚡What is movie-web? + +movie-web is a web app for watching movies easily. Check it out at movie-web.app. + +This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface. + +# 🔥Features + +- Automatic saving of progress - optionally synced to an account. +- Bookmark shows or movies, keep track of what you want to watch. +- Minimalistic interface that only shows whats required - no algorithm to consume you. + +## 🍄 Philosophy + +This project is meant to be simple and easy to use. Keep features minimal but polished. +We do not want this project to be yet another bulky streaming site, instead it aims for minimalism. + +On top of that, hosting should be as cheap and simple as possible. Just a static website with a proxy, with an optional backend if you want cross-device syncing. + +Content is fetched from third parties and scraping is fully done on the client. This means that the hoster has no files or media on their server. All files are streamed directly from the third parties. + +## ⚠️ Limitations + +- Due to being a static site, there can be no SSR +- To keep it cheap to host, amount of proxied requests need to be kept to a minimum +- Also to keep it cheap, no content must ever be streamed through the proxy. So only streams not protected by CORS headers. + +# 🧬 Running locally for development + +To run locally, you must first clone the repository. After that run the following commands in the root of the repository: +```bash +pnpm install +pnpm run dev +``` + +You have to also make an `.env` file to configure your environment. Inspire it from the content of `example.env`. + +To build production files, run: +```bash +pnpm build +``` + +> [!TIP] +> You must use pnpm (`npm i -g pnpm`) and run NodeJS 20 + +# 🥔 Selfhosting + +A simple guide has been written to assist in hosting your own instance of movie-web. Check it out below + +|[Selfhosting guide](https://docs.movie-web.app)| +|---| + +## 🤝 Thanks to all Contributors +This project would not be possible without our amazing contributors and the community. Thanks a lot! Keep rocking 🍻. + +[![Contributors](https://contrib.rocks/image?repo=movie-web/movie-web)](https://github.com/movie-web/movie-web/graphs/contributors) + diff --git a/example.env b/example.env new file mode 100644 index 00000000..65e2829f --- /dev/null +++ b/example.env @@ -0,0 +1,8 @@ +VITE_TMDB_READ_API_KEY=eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJiZmU0OGY4NjFkY2NmMjczMzUyMDdmMWVjYmVkNjNjNiIsInN1YiI6IjY1YjNmMWI0NTk0Yzk0MDE2MzNkZDBjNSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.GiCKswc2u9NraBbujm0ykI5G3p-K9WJoHg40jYbFv4o +VITE_OPENSEARCH_ENABLED=false + +# make sure the cors proxy url does NOT have a slash at the end +VITE_CORS_PROXY_URL=https://flixweb-proxy.netlify.app + +# make sure the domain does NOT have a slash at the end +VITE_APP_DOMAIN=http://localhost:5173 diff --git a/index.html b/index.html new file mode 100644 index 00000000..4d7ea1cf --- /dev/null +++ b/index.html @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sudo Flix + + {{#if opensearchEnabled }} + + + + + + {{/if}} + + + + +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 00000000..d1ebc1c7 --- /dev/null +++ b/package.json @@ -0,0 +1,137 @@ +{ + "name": "sudo-flix", + "version": "4.4.2", + "private": true, + "homepage": "https://movie-web.app", + "scripts": { + "dev": "vite", + "build": "vite build", + "build:pwa": "cross-env VITE_PWA_ENABLED=true vite build", + "test": "vitest run", + "preview": "vite preview", + "lint": "eslint --ext .tsx,.ts src", + "lint:fix": "eslint --fix --ext .tsx,.ts src", + "lint:report": "eslint --ext .tsx,.ts --output-file eslint_report.json --format json src", + "preinstall": "npx -y only-allow pnpm" + }, + "browserslist": { + "production": [ + "defaults", + "chrome > 90" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "dependencies": { + "@formkit/auto-animate": "^0.8.1", + "@headlessui/react": "^1.7.17", + "@ladjs/country-language": "^1.0.3", + "@movie-web/providers": "^2.2.0", + "@noble/hashes": "^1.3.3", + "@plasmohq/messaging": "^0.6.1", + "@react-spring/web": "^9.7.3", + "@scure/bip39": "^1.2.2", + "@sozialhelden/ietf-language-tags": "^5.4.2", + "@types/node-forge": "^1.3.10", + "classnames": "^2.3.2", + "core-js": "^3.34.0", + "detect-browser": "^5.3.0", + "dompurify": "^3.0.6", + "flag-icons": "^7.1.0", + "focus-trap-react": "^10.2.3", + "fscreen": "^1.2.0", + "fuse.js": "^7.0.0", + "hls.js": "^1.4.14", + "i18next": "^23.7.11", + "immer": "^10.0.3", + "jwt-decode": "^4.0.0", + "lodash.isequal": "^4.5.0", + "million": "^2.6.4", + "nanoid": "^5.0.4", + "node-forge": "^1.3.1", + "ofetch": "^1.3.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-ga4": "^2.1.0", + "react-google-recaptcha-v3": "^1.10.1", + "react-helmet-async": "^2.0.4", + "react-i18next": "^14.0.0", + "react-lazy-with-preload": "^2.2.1", + "react-router-dom": "^6.21.1", + "react-sticky-el": "^2.1.0", + "react-turnstile": "^1.1.2", + "react-use": "^17.4.2", + "semver": "^7.5.4", + "slugify": "^1.6.6", + "subsrt-ts": "^2.1.2", + "zustand": "^4.4.7" + }, + "devDependencies": { + "@babel/core": "^7.23.6", + "@babel/preset-env": "^7.23.6", + "@babel/preset-typescript": "^7.23.3", + "@rollup/wasm-node": "^4.9.4", + "@types/chromecast-caf-sender": "^1.0.8", + "@types/crypto-js": "^4.2.1", + "@types/dompurify": "^3.0.5", + "@types/fscreen": "^1.0.4", + "@types/lodash.isequal": "^4.5.8", + "@types/lodash.throttle": "^4.1.9", + "@types/node": "^20.10.5", + "@types/pako": "^2.0.3", + "@types/react": "^18.2.45", + "@types/react-dom": "^18.2.18", + "@types/react-helmet": "^6.1.11", + "@types/react-router": "^5.1.20", + "@types/react-router-dom": "^5.3.3", + "@types/react-stickynode": "^4.0.3", + "@types/react-transition-group": "^4.4.10", + "@types/semver": "^7.5.6", + "@typescript-eslint/eslint-plugin": "^6.15.0", + "@typescript-eslint/parser": "^6.15.0", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.16", + "cross-env": "^7.0.3", + "eslint": "^8.56.0", + "eslint-config-airbnb": "19.0.4", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-prettier": "^5.1.1", + "eslint-plugin-react": "7.33.2", + "eslint-plugin-react-hooks": "4.6.0", + "glob": "^10.3.10", + "handlebars": "^4.7.8", + "jsdom": "^23.0.1", + "postcss": "^8.4.32", + "postcss-rtl": "^2.0.0", + "postcss-rtlcss": "^4.0.9", + "prettier": "^3.1.1", + "prettier-plugin-tailwindcss": "^0.5.9", + "rollup-plugin-visualizer": "^5.11.0", + "tailwind-scrollbar": "^3.0.5", + "tailwindcss": "^3.4.0", + "tailwindcss-themer": "^4.0.0", + "type-fest": "^4.8.3", + "typescript": "^5.3.3", + "vite": "^5.0.12", + "vite-plugin-checker": "^0.6.2", + "vite-plugin-package-version": "^1.1.0", + "vite-plugin-pwa": "^0.17.4", + "vite-plugin-static-copy": "^1.0.0", + "vitest": "^1.1.0" + }, + "pnpm": { + "overrides": { + "get-func-name@<2.0.1": ">=2.0.1", + "postcss@<8.4.31": ">=8.4.31", + "@babel/traverse@<7.23.2": ">=7.23.2", + "crypto-js@<4.2.0": ">=4.2.0", + "rollup": "npm:@rollup/wasm-node" + } + } +} diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 00000000..e9ee2f2d --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1 @@ +figmaTokens.json diff --git a/plugins/figmaTokensToThemeTokens.mjs b/plugins/figmaTokensToThemeTokens.mjs new file mode 100644 index 00000000..5be386d0 --- /dev/null +++ b/plugins/figmaTokensToThemeTokens.mjs @@ -0,0 +1,43 @@ +/** + * This script turns output from the figma plugin "style to JSON" into a usuable theme. + * It expects a format of "themes/{NAME}/anythinghere" + */ + +import fs from "fs"; + +const fileLocation = "./figmaTokens.json"; +const theme = "blue"; + +const fileContents = fs.readFileSync(fileLocation, { + encoding: "utf-8" +}); +const tokens = JSON.parse(fileContents); + +const themeTokens = tokens.themes[theme]; +const output = {}; + +function setKey(obj, key, defaultVal) { + const realKey = key.match(/^\d+$/g) ? "c" + key : key; + if (obj[key]) return obj[key]; + obj[realKey] = defaultVal; + return obj[realKey]; +} + +function handleToken(token, path) { + if (typeof token.name === "string" && typeof token.description === "string") { + let ref = output; + const lastKey = path.pop(); + path.forEach((v) => { + ref = setKey(ref, v, {}); + }); + setKey(ref, lastKey, token.hex); + return; + } + + for (let key in token) { + handleToken(token[key], [...path, key]); + } +} + +handleToken(themeTokens, []); +console.log(JSON.stringify(output, null, 2)); diff --git a/plugins/handlebars.ts b/plugins/handlebars.ts new file mode 100644 index 00000000..a8ab0170 --- /dev/null +++ b/plugins/handlebars.ts @@ -0,0 +1,41 @@ +import { globSync } from "glob"; +import { viteStaticCopy } from 'vite-plugin-static-copy' +import { PluginOption } from "vite"; +import Handlebars from "handlebars"; +import path from "path"; + +export const handlebars = (options: { vars?: Record } = {}): PluginOption[] => { + const files = globSync("src/assets/**/**.hbs"); + + function render(content: string): string { + const template = Handlebars.compile(content); + return template(options?.vars ?? {}); + } + + return [ + { + name: 'hbs-templating', + enforce: "pre", + transformIndexHtml: { + order: 'pre', + handler(html) { + return render(html); + } + }, + }, + viteStaticCopy({ + silent: true, + targets: files.map(file => ({ + src: file, + dest: '', + rename: path.basename(file).slice(0, -4), // remove .hbs file extension + transform: { + encoding: 'utf8', + handler(content: string) { + return render(content); + } + } + })) + }) + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..97b59603 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,7534 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + get-func-name@<2.0.1: '>=2.0.1' + postcss@<8.4.31: '>=8.4.31' + '@babel/traverse@<7.23.2': '>=7.23.2' + crypto-js@<4.2.0: '>=4.2.0' + rollup: npm:@rollup/wasm-node + +dependencies: + '@formkit/auto-animate': + specifier: ^0.8.1 + version: 0.8.1 + '@headlessui/react': + specifier: ^1.7.17 + version: 1.7.17(react-dom@18.2.0)(react@18.2.0) + '@ladjs/country-language': + specifier: ^1.0.3 + version: 1.0.3 + '@movie-web/providers': + specifier: ^2.2.0 + version: 2.2.0 + '@noble/hashes': + specifier: ^1.3.3 + version: 1.3.3 + '@plasmohq/messaging': + specifier: ^0.6.1 + version: 0.6.1(react@18.2.0) + '@react-spring/web': + specifier: ^9.7.3 + version: 9.7.3(react-dom@18.2.0)(react@18.2.0) + '@scure/bip39': + specifier: ^1.2.2 + version: 1.2.2 + '@sozialhelden/ietf-language-tags': + specifier: ^5.4.2 + version: 5.4.2 + '@types/node-forge': + specifier: ^1.3.10 + version: 1.3.10 + classnames: + specifier: ^2.3.2 + version: 2.3.2 + core-js: + specifier: ^3.34.0 + version: 3.34.0 + detect-browser: + specifier: ^5.3.0 + version: 5.3.0 + dompurify: + specifier: ^3.0.6 + version: 3.0.6 + flag-icons: + specifier: ^7.1.0 + version: 7.1.0 + focus-trap-react: + specifier: ^10.2.3 + version: 10.2.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) + fscreen: + specifier: ^1.2.0 + version: 1.2.0 + fuse.js: + specifier: ^7.0.0 + version: 7.0.0 + hls.js: + specifier: ^1.4.14 + version: 1.4.14 + i18next: + specifier: ^23.7.11 + version: 23.7.11 + immer: + specifier: ^10.0.3 + version: 10.0.3 + jwt-decode: + specifier: ^4.0.0 + version: 4.0.0 + lodash.isequal: + specifier: ^4.5.0 + version: 4.5.0 + million: + specifier: ^2.6.4 + version: 2.6.4 + nanoid: + specifier: ^5.0.4 + version: 5.0.4 + node-forge: + specifier: ^1.3.1 + version: 1.3.1 + ofetch: + specifier: ^1.3.3 + version: 1.3.3 + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + react-ga4: + specifier: ^2.1.0 + version: 2.1.0 + react-google-recaptcha-v3: + specifier: ^1.10.1 + version: 1.10.1(react-dom@18.2.0)(react@18.2.0) + react-helmet-async: + specifier: ^2.0.4 + version: 2.0.4(react-dom@18.2.0)(react@18.2.0) + react-i18next: + specifier: ^14.0.0 + version: 14.0.0(i18next@23.7.11)(react-dom@18.2.0)(react@18.2.0) + react-lazy-with-preload: + specifier: ^2.2.1 + version: 2.2.1 + react-router-dom: + specifier: ^6.21.1 + version: 6.21.1(react-dom@18.2.0)(react@18.2.0) + react-sticky-el: + specifier: ^2.1.0 + version: 2.1.0(react-dom@18.2.0)(react@18.2.0) + react-turnstile: + specifier: ^1.1.2 + version: 1.1.2(react-dom@18.2.0)(react@18.2.0) + react-use: + specifier: ^17.4.2 + version: 17.4.2(react-dom@18.2.0)(react@18.2.0) + semver: + specifier: ^7.5.4 + version: 7.5.4 + slugify: + specifier: ^1.6.6 + version: 1.6.6 + subsrt-ts: + specifier: ^2.1.2 + version: 2.1.2 + zustand: + specifier: ^4.4.7 + version: 4.4.7(@types/react@18.2.45)(immer@10.0.3)(react@18.2.0) + +devDependencies: + '@babel/core': + specifier: ^7.23.6 + version: 7.23.6 + '@babel/preset-env': + specifier: ^7.23.6 + version: 7.23.6(@babel/core@7.23.6) + '@babel/preset-typescript': + specifier: ^7.23.3 + version: 7.23.3(@babel/core@7.23.6) + '@rollup/wasm-node': + specifier: ^4.9.4 + version: 4.9.4 + '@types/chromecast-caf-sender': + specifier: ^1.0.8 + version: 1.0.8 + '@types/crypto-js': + specifier: ^4.2.1 + version: 4.2.1 + '@types/dompurify': + specifier: ^3.0.5 + version: 3.0.5 + '@types/fscreen': + specifier: ^1.0.4 + version: 1.0.4 + '@types/lodash.isequal': + specifier: ^4.5.8 + version: 4.5.8 + '@types/lodash.throttle': + specifier: ^4.1.9 + version: 4.1.9 + '@types/node': + specifier: ^20.10.5 + version: 20.10.5 + '@types/pako': + specifier: ^2.0.3 + version: 2.0.3 + '@types/react': + specifier: ^18.2.45 + version: 18.2.45 + '@types/react-dom': + specifier: ^18.2.18 + version: 18.2.18 + '@types/react-helmet': + specifier: ^6.1.11 + version: 6.1.11 + '@types/react-router': + specifier: ^5.1.20 + version: 5.1.20 + '@types/react-router-dom': + specifier: ^5.3.3 + version: 5.3.3 + '@types/react-stickynode': + specifier: ^4.0.3 + version: 4.0.3 + '@types/react-transition-group': + specifier: ^4.4.10 + version: 4.4.10 + '@types/semver': + specifier: ^7.5.6 + version: 7.5.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.15.0 + version: 6.15.0(@typescript-eslint/parser@6.15.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: ^6.15.0 + version: 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@vitejs/plugin-react': + specifier: ^4.2.1 + version: 4.2.1(vite@5.0.12) + autoprefixer: + specifier: ^10.4.16 + version: 10.4.16(postcss@8.4.32) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + eslint: + specifier: ^8.56.0 + version: 8.56.0 + eslint-config-airbnb: + specifier: 19.0.4 + version: 19.0.4(eslint-plugin-import@2.29.1)(eslint-plugin-jsx-a11y@6.8.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.33.2)(eslint@8.56.0) + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.56.0) + eslint-import-resolver-typescript: + specifier: ^3.6.1 + version: 3.6.1(@typescript-eslint/parser@6.15.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-plugin-import: + specifier: ^2.29.1 + version: 2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-jsx-a11y: + specifier: ^6.8.0 + version: 6.8.0(eslint@8.56.0) + eslint-plugin-prettier: + specifier: ^5.1.1 + version: 5.1.1(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.1.1) + eslint-plugin-react: + specifier: 7.33.2 + version: 7.33.2(eslint@8.56.0) + eslint-plugin-react-hooks: + specifier: 4.6.0 + version: 4.6.0(eslint@8.56.0) + glob: + specifier: ^10.3.10 + version: 10.3.10 + handlebars: + specifier: ^4.7.8 + version: 4.7.8 + jsdom: + specifier: ^23.0.1 + version: 23.0.1 + postcss: + specifier: ^8.4.32 + version: 8.4.32 + postcss-rtl: + specifier: ^2.0.0 + version: 2.0.0(postcss@8.4.32) + postcss-rtlcss: + specifier: ^4.0.9 + version: 4.0.9(postcss@8.4.32) + prettier: + specifier: ^3.1.1 + version: 3.1.1 + prettier-plugin-tailwindcss: + specifier: ^0.5.9 + version: 0.5.9(prettier@3.1.1) + rollup-plugin-visualizer: + specifier: ^5.11.0 + version: 5.11.0(@rollup/wasm-node@4.12.0) + tailwind-scrollbar: + specifier: ^3.0.5 + version: 3.0.5(tailwindcss@3.4.0) + tailwindcss: + specifier: ^3.4.0 + version: 3.4.0 + tailwindcss-themer: + specifier: ^4.0.0 + version: 4.0.0(tailwindcss@3.4.0) + type-fest: + specifier: ^4.8.3 + version: 4.8.3 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + vite: + specifier: ^5.0.12 + version: 5.0.12(@types/node@20.10.5) + vite-plugin-checker: + specifier: ^0.6.2 + version: 0.6.2(eslint@8.56.0)(typescript@5.3.3)(vite@5.0.12) + vite-plugin-package-version: + specifier: ^1.1.0 + version: 1.1.0(vite@5.0.12) + vite-plugin-pwa: + specifier: ^0.17.4 + version: 0.17.4(vite@5.0.12)(workbox-build@7.0.0)(workbox-window@7.0.0) + vite-plugin-static-copy: + specifier: ^1.0.0 + version: 1.0.0(vite@5.0.12) + vitest: + specifier: ^1.1.0 + version: 1.1.0(@types/node@20.10.5)(jsdom@23.0.1) + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + + /@apideck/better-ajv-errors@0.3.6(ajv@8.12.0): + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + dependencies: + ajv: 8.12.0 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + dev: true + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.13 + chalk: 2.4.2 + dev: true + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + engines: {node: '>=6.9.0'} + + /@babel/core@7.23.6: + resolution: {integrity: sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) + '@babel/helpers': 7.23.6 + '@babel/parser': 7.23.6 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.6 + '@babel/types': 7.23.6 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + /@babel/generator@7.23.6: + resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.6 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + jsesc: 2.5.2 + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: + resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.22.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + /@babel/helper-create-class-features-plugin@7.23.6(@babel/core@7.23.6): + resolution: {integrity: sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.6) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + semver: 6.3.1 + dev: true + + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.6): + resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-annotate-as-pure': 7.22.5 + regexpu-core: 5.3.2 + semver: 6.3.1 + dev: true + + /@babel/helper-create-regexp-features-plugin@7.22.9(@babel/core@7.23.6): + resolution: {integrity: sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-annotate-as-pure': 7.22.5 + regexpu-core: 5.3.2 + semver: 6.3.1 + dev: true + + /@babel/helper-define-polyfill-provider@0.4.4(@babel/core@7.23.6): + resolution: {integrity: sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + debug: 4.3.4 + lodash.debounce: 4.0.8 + resolve: 1.22.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + + /@babel/helper-optimise-call-expression@7.22.5: + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.6): + resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-wrap-function': 7.22.20 + dev: true + + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.6): + resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + + /@babel/helper-skip-transparent-expression-wrappers@7.22.5: + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + + /@babel/helper-wrap-function@7.22.20: + resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-function-name': 7.23.0 + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + dev: true + + /@babel/helpers@7.23.6: + resolution: {integrity: sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.6 + '@babel/types': 7.23.6 + transitivePeerDependencies: + - supports-color + + /@babel/highlight@7.22.13: + resolution: {integrity: sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/parser@7.23.6: + resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.6 + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.6) + dev: true + + /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.6): + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.6): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.6): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.6): + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.6): + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.6): + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.6): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.6): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.6): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.6): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.6): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.6): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.6): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.6): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.6): + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.6): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.6): + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.6) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-classes@7.23.5(@babel/core@7.23.6): + resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.6) + '@babel/helper-split-export-declaration': 7.22.6 + globals: 11.12.0 + dev: true + + /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/template': 7.22.15 + dev: true + + /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-for-of@7.23.6(@babel/core@7.23.6): + resolution: {integrity: sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + dev: true + + /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + dev: true + + /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.6): + resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.6): + resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-react-jsx-self@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-react-jsx-source@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + regenerator-transform: 0.15.2 + dev: true + + /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + dev: true + + /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.23.6): + resolution: {integrity: sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6) + dev: true + + /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.6) + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/preset-env@7.23.6(@babel/core@7.23.6): + resolution: {integrity: sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.6 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.6) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.6) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.6) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.6) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.6) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.6) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.6) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.6) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.6) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.6) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.6) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.23.6) + '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-for-of': 7.23.6(@babel/core@7.23.6) + '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.6) + '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.6) + '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.6) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.6) + babel-plugin-polyfill-corejs2: 0.4.7(@babel/core@7.23.6) + babel-plugin-polyfill-corejs3: 0.8.7(@babel/core@7.23.6) + babel-plugin-polyfill-regenerator: 0.5.4(@babel/core@7.23.6) + core-js-compat: 3.32.1 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.6): + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/types': 7.23.0 + esutils: 2.0.3 + dev: true + + /@babel/preset-typescript@7.23.3(@babel/core@7.23.6): + resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.23.5 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.23.6) + dev: true + + /@babel/regjsgen@0.8.0: + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + dev: true + + /@babel/runtime@7.22.11: + resolution: {integrity: sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + + /@babel/runtime@7.23.6: + resolution: {integrity: sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + + /@babel/traverse@7.23.6: + resolution: {integrity: sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.6 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + + /@babel/types@7.23.6: + resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + + /@esbuild/aix-ppc64@0.19.10: + resolution: {integrity: sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.19.10: + resolution: {integrity: sha512-1X4CClKhDgC3by7k8aOWZeBXQX8dHT5QAMCAQDArCLaYfkppoARvh0fit3X2Qs+MXDngKcHv6XXyQCpY0hkK1Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.19.10: + resolution: {integrity: sha512-7W0bK7qfkw1fc2viBfrtAEkDKHatYfHzr/jKAHNr9BvkYDXPcC6bodtm8AyLJNNuqClLNaeTLuwURt4PRT9d7w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.19.10: + resolution: {integrity: sha512-O/nO/g+/7NlitUxETkUv/IvADKuZXyH4BHf/g/7laqKC4i/7whLpB0gvpPc2zpF0q9Q6FXS3TS75QHac9MvVWw==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.19.10: + resolution: {integrity: sha512-YSRRs2zOpwypck+6GL3wGXx2gNP7DXzetmo5pHXLrY/VIMsS59yKfjPizQ4lLt5vEI80M41gjm2BxrGZ5U+VMA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.19.10: + resolution: {integrity: sha512-alfGtT+IEICKtNE54hbvPg13xGBe4GkVxyGWtzr+yHO7HIiRJppPDhOKq3zstTcVf8msXb/t4eavW3jCDpMSmA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.19.10: + resolution: {integrity: sha512-dMtk1wc7FSH8CCkE854GyGuNKCewlh+7heYP/sclpOG6Cectzk14qdUIY5CrKDbkA/OczXq9WesqnPl09mj5dg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.19.10: + resolution: {integrity: sha512-G5UPPspryHu1T3uX8WiOEUa6q6OlQh6gNl4CO4Iw5PS+Kg5bVggVFehzXBJY6X6RSOMS8iXDv2330VzaObm4Ag==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.19.10: + resolution: {integrity: sha512-QxaouHWZ+2KWEj7cGJmvTIHVALfhpGxo3WLmlYfJ+dA5fJB6lDEIg+oe/0//FuyVHuS3l79/wyBxbHr0NgtxJQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.19.10: + resolution: {integrity: sha512-j6gUW5aAaPgD416Hk9FHxn27On28H4eVI9rJ4az7oCGTFW48+LcgNDBN+9f8rKZz7EEowo889CPKyeaD0iw9Kg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.19.10: + resolution: {integrity: sha512-4ub1YwXxYjj9h1UIZs2hYbnTZBtenPw5NfXCRgEkGb0b6OJ2gpkMvDqRDYIDRjRdWSe/TBiZltm3Y3Q8SN1xNg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.19.10: + resolution: {integrity: sha512-lo3I9k+mbEKoxtoIbM0yC/MZ1i2wM0cIeOejlVdZ3D86LAcFXFRdeuZmh91QJvUTW51bOK5W2BznGNIl4+mDaA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.19.10: + resolution: {integrity: sha512-J4gH3zhHNbdZN0Bcr1QUGVNkHTdpijgx5VMxeetSk6ntdt+vR1DqGmHxQYHRmNb77tP6GVvD+K0NyO4xjd7y4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.19.10: + resolution: {integrity: sha512-tgT/7u+QhV6ge8wFMzaklOY7KqiyitgT1AUHMApau32ZlvTB/+efeCtMk4eXS+uEymYK249JsoiklZN64xt6oQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.19.10: + resolution: {integrity: sha512-0f/spw0PfBMZBNqtKe5FLzBDGo0SKZKvMl5PHYQr3+eiSscfJ96XEknCe+JoOayybWUFQbcJTrk946i3j9uYZA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.19.10: + resolution: {integrity: sha512-pZFe0OeskMHzHa9U38g+z8Yx5FNCLFtUnJtQMpwhS+r4S566aK2ci3t4NCP4tjt6d5j5uo4h7tExZMjeKoehAA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.19.10: + resolution: {integrity: sha512-SpYNEqg/6pZYoc+1zLCjVOYvxfZVZj6w0KROZ3Fje/QrM3nfvT2llI+wmKSrWuX6wmZeTapbarvuNNK/qepSgA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.19.10: + resolution: {integrity: sha512-ACbZ0vXy9zksNArWlk2c38NdKg25+L9pr/mVaj9SUq6lHZu/35nx2xnQVRGLrC1KKQqJKRIB0q8GspiHI3J80Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.19.10: + resolution: {integrity: sha512-PxcgvjdSjtgPMiPQrM3pwSaG4kGphP+bLSb+cihuP0LYdZv1epbAIecHVl5sD3npkfYBZ0ZnOjR878I7MdJDFg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.19.10: + resolution: {integrity: sha512-ZkIOtrRL8SEJjr+VHjmW0znkPs+oJXhlJbNwfI37rvgeMtk3sxOQevXPXjmAPZPigVTncvFqLMd+uV0IBSEzqA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.19.10: + resolution: {integrity: sha512-+Sa4oTDbpBfGpl3Hn3XiUe4f8TU2JF7aX8cOfqFYMMjXp6ma6NJDztl5FDG8Ezx0OjwGikIHw+iA54YLDNNVfw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.19.10: + resolution: {integrity: sha512-EOGVLK1oWMBXgfttJdPHDTiivYSjX6jDNaATeNOaCOFEVcfMjtbx7WVQwPSE1eIfCp/CaSF2nSrDtzc4I9f8TQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.19.10: + resolution: {integrity: sha512-whqLG6Sc70AbU73fFYvuYzaE4MNMBIlR1Y/IrUeOXFrWHxBEjjbZaQ3IXIQS8wJdAzue2GwYZCjOrgrU1oUHoA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.56.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.8.0: + resolution: {integrity: sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.21.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.56.0: + resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@formkit/auto-animate@0.8.1: + resolution: {integrity: sha512-0/Z2cuNXWVVIG/l0SpcHAWFhGdvLJ8DRvEfRWvmojtmRWfEy+LWNwgDazbZqY0qQYtkHcoEK3jBLkhiZaB/4Ig==} + dev: false + + /@headlessui/react@1.7.17(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + dependencies: + client-only: 0.0.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + + /@jridgewell/source-map@0.3.5: + resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + + /@ladjs/country-language@1.0.3: + resolution: {integrity: sha512-FJROu9/hh4eqVAGDyfL8vpv6Vb0qKHX1ozYLRZ+beUzD5xFf+3r0J+SVIWKviEa7W524Qvqou+ta1WrsRgzxGw==} + engines: {node: '>= 14'} + dev: false + + /@movie-web/providers@2.2.0: + resolution: {integrity: sha512-7rKUpLPklwOtS5P2CAeh0P3sPIuYvtkKIgm0kVMp+OsSpKd9IcuYm79bbDrA0MDi3IMGik1W6la9Mzy91+8uYQ==} + dependencies: + cheerio: 1.0.0-rc.12 + cookie: 0.6.0 + crypto-js: 4.2.0 + form-data: 4.0.0 + iso-639-1: 3.1.0 + nanoid: 3.3.7 + node-fetch: 2.7.0 + set-cookie-parser: 2.6.0 + unpacker: 1.0.1 + transitivePeerDependencies: + - encoding + dev: false + + /@noble/hashes@1.3.3: + resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} + engines: {node: '>= 16'} + dev: false + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + + /@pkgr/utils@2.4.2: + resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dependencies: + cross-spawn: 7.0.3 + fast-glob: 3.3.1 + is-glob: 4.0.3 + open: 9.1.0 + picocolors: 1.0.0 + tslib: 2.6.2 + dev: true + + /@plasmohq/messaging@0.6.1(react@18.2.0): + resolution: {integrity: sha512-/nn1k8SG5z++o/NnZu+byHWcC9MhPLxfmvj+AP3buqMn7uwfYDcYWURLuMW2Knw08HBg+wku2v1Ltt4evN0nzA==} + peerDependencies: + react: ^16.8.6 || ^17 || ^18 + peerDependenciesMeta: + react: + optional: true + dependencies: + nanoid: 5.0.3 + react: 18.2.0 + dev: false + + /@react-spring/animated@9.7.3(react@18.2.0): + resolution: {integrity: sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@react-spring/shared': 9.7.3(react@18.2.0) + '@react-spring/types': 9.7.3 + react: 18.2.0 + dev: false + + /@react-spring/core@9.7.3(react@18.2.0): + resolution: {integrity: sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@react-spring/animated': 9.7.3(react@18.2.0) + '@react-spring/shared': 9.7.3(react@18.2.0) + '@react-spring/types': 9.7.3 + react: 18.2.0 + dev: false + + /@react-spring/shared@9.7.3(react@18.2.0): + resolution: {integrity: sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@react-spring/types': 9.7.3 + react: 18.2.0 + dev: false + + /@react-spring/types@9.7.3: + resolution: {integrity: sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==} + dev: false + + /@react-spring/web@9.7.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@react-spring/animated': 9.7.3(react@18.2.0) + '@react-spring/core': 9.7.3(react@18.2.0) + '@react-spring/shared': 9.7.3(react@18.2.0) + '@react-spring/types': 9.7.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@remix-run/router@1.14.1: + resolution: {integrity: sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==} + engines: {node: '>=14.0.0'} + dev: false + + /@rollup/plugin-babel@5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.12.0): + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: npm:@rollup/wasm-node + peerDependenciesMeta: + '@types/babel__core': + optional: true + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-module-imports': 7.22.15 + '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.0) + rollup: /@rollup/wasm-node@4.12.0 + dev: true + + /@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.12.0): + resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: npm:@rollup/wasm-node + dependencies: + '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.0) + '@types/resolve': 1.17.1 + builtin-modules: 3.3.0 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.4 + rollup: /@rollup/wasm-node@4.12.0 + dev: true + + /@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.12.0): + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: npm:@rollup/wasm-node + dependencies: + '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.0) + magic-string: 0.25.9 + rollup: /@rollup/wasm-node@4.12.0 + dev: true + + /@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.12.0): + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: npm:@rollup/wasm-node + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: /@rollup/wasm-node@4.12.0 + dev: true + + /@rollup/wasm-node@4.12.0: + resolution: {integrity: sha512-sqy3+YvV/uWX6bPZOR5PlEdH6xyMPXoelllRQ/uZ13tzy9f4pXZTbajnoWN8IHHXwTNKPiLzsePLiDEVmkxMNw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + fsevents: 2.3.3 + + /@rollup/wasm-node@4.9.4: + resolution: {integrity: sha512-K9ZPYMCxP7sBElj5du0En/zpbhXTQxpWI7RlF+8bNpLUozhzg2Pcx2h3cBCzV7xtiUt0dc+pF2Ib3/Sg8R0JMA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /@scure/base@1.1.5: + resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==} + dev: false + + /@scure/bip39@1.2.2: + resolution: {integrity: sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==} + dependencies: + '@noble/hashes': 1.3.3 + '@scure/base': 1.1.5 + dev: false + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@sozialhelden/ietf-language-tags@5.4.2: + resolution: {integrity: sha512-aCN7bVOfX9sBN0EHyWJT14H8bx+VYBo8tdcynai35wgoxKMfVtgEECkQ1gs8nEL6GHGes8lPIfo6AjIch44N3w==} + dependencies: + lodash.compact: 3.0.1 + typescript: 4.9.5 + dev: false + + /@surma/rollup-plugin-off-main-thread@2.2.3: + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} + dependencies: + ejs: 3.1.9 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.9 + dev: true + + /@types/babel__core@7.20.5: + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.4 + dev: true + + /@types/babel__generator@7.6.8: + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + dev: true + + /@types/babel__traverse@7.20.4: + resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/chrome@0.0.244: + resolution: {integrity: sha512-pC8kBWuUqGhG6S5d3E44a2f1TA2XHX/sXfCIrrDpA4K65Ozzw3mYQjuvMOTdSUCx4HjJhp4zuZh6fKOH3UdA1g==} + dependencies: + '@types/filesystem': 0.0.32 + '@types/har-format': 1.2.12 + dev: true + + /@types/chromecast-caf-sender@1.0.8: + resolution: {integrity: sha512-3vByTZptIkYpNz/wErOnpFQ4hWGU1ghO5bcivNButxARbXXKfClLJv6r5Pd622nD/UecjSwrAeIposAIzd4HvA==} + dependencies: + '@types/chrome': 0.0.244 + dev: true + + /@types/crypto-js@4.2.1: + resolution: {integrity: sha512-FSPGd9+OcSok3RsM0UZ/9fcvMOXJ1ENE/ZbLfOPlBWj7BgXtEAM8VYfTtT760GiLbQIMoVozwVuisjvsVwqYWw==} + dev: true + + /@types/dompurify@3.0.5: + resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==} + dependencies: + '@types/trusted-types': 2.0.3 + dev: true + + /@types/estree@0.0.39: + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + /@types/filesystem@0.0.32: + resolution: {integrity: sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==} + dependencies: + '@types/filewriter': 0.0.29 + dev: true + + /@types/filewriter@0.0.29: + resolution: {integrity: sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==} + dev: true + + /@types/fscreen@1.0.4: + resolution: {integrity: sha512-TsjxyAUvlvuQyao9vNk0yES4nY07K9xoAbkhgXU948JG39EqlLxniWuW9OiZde9Q8ACSpu3fmbXXRAfb/l/HqQ==} + dev: true + + /@types/har-format@1.2.12: + resolution: {integrity: sha512-P20p/YBrqUBmzD6KhIQ8EiY4/RRzlekL4eCvfQnulFPfjmiGxKIoyCeI7qam5I7oKH3P8EU4ptEi0EfyGoLysw==} + dev: true + + /@types/history@4.7.11: + resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} + dev: true + + /@types/js-cookie@2.2.7: + resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} + dev: false + + /@types/json-schema@7.0.12: + resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + dev: true + + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/lodash.isequal@4.5.8: + resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + dependencies: + '@types/lodash': 4.14.197 + dev: true + + /@types/lodash.throttle@4.1.9: + resolution: {integrity: sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==} + dependencies: + '@types/lodash': 4.14.197 + dev: true + + /@types/lodash@4.14.197: + resolution: {integrity: sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==} + dev: true + + /@types/node-forge@1.3.10: + resolution: {integrity: sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==} + dependencies: + '@types/node': 20.10.5 + dev: false + + /@types/node@20.10.5: + resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==} + dependencies: + undici-types: 5.26.5 + + /@types/pako@2.0.3: + resolution: {integrity: sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==} + dev: true + + /@types/prop-types@15.7.5: + resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + + /@types/react-dom@18.2.18: + resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} + dependencies: + '@types/react': 18.2.45 + dev: true + + /@types/react-helmet@6.1.11: + resolution: {integrity: sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==} + dependencies: + '@types/react': 18.2.45 + dev: true + + /@types/react-router-dom@5.3.3: + resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} + dependencies: + '@types/history': 4.7.11 + '@types/react': 18.2.45 + '@types/react-router': 5.1.20 + dev: true + + /@types/react-router@5.1.20: + resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} + dependencies: + '@types/history': 4.7.11 + '@types/react': 18.2.45 + dev: true + + /@types/react-stickynode@4.0.3: + resolution: {integrity: sha512-K7YkwdhXQE4YVxIVweix4nkpdG4onm/dcnKK+qCj0vgUrNiKng+09zOfjF5AlOcC1HQkg5yxVLwp/0AzT84R0w==} + dependencies: + '@types/react': 18.2.45 + dev: true + + /@types/react-transition-group@4.4.10: + resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} + dependencies: + '@types/react': 18.2.45 + dev: true + + /@types/react@18.2.45: + resolution: {integrity: sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==} + dependencies: + '@types/prop-types': 15.7.5 + '@types/scheduler': 0.16.3 + csstype: 3.1.2 + + /@types/resolve@1.17.1: + resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} + dependencies: + '@types/node': 20.10.5 + dev: true + + /@types/scheduler@0.16.3: + resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} + + /@types/semver@7.5.6: + resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + dev: true + + /@types/trusted-types@2.0.3: + resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} + dev: true + + /@typescript-eslint/eslint-plugin@6.15.0(@typescript-eslint/parser@6.15.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.8.0 + '@typescript-eslint/parser': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.15.0 + '@typescript-eslint/type-utils': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.15.0 + debug: 4.3.4 + eslint: 8.56.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.15.0 + '@typescript-eslint/types': 6.15.0 + '@typescript-eslint/typescript-estree': 6.15.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.15.0 + debug: 4.3.4 + eslint: 8.56.0 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.15.0: + resolution: {integrity: sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.15.0 + '@typescript-eslint/visitor-keys': 6.15.0 + dev: true + + /@typescript-eslint/type-utils@6.15.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.15.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + debug: 4.3.4 + eslint: 8.56.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.15.0: + resolution: {integrity: sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.15.0(typescript@5.3.3): + resolution: {integrity: sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.15.0 + '@typescript-eslint/visitor-keys': 6.15.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.15.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.6 + '@typescript-eslint/scope-manager': 6.15.0 + '@typescript-eslint/types': 6.15.0 + '@typescript-eslint/typescript-estree': 6.15.0(typescript@5.3.3) + eslint: 8.56.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.15.0: + resolution: {integrity: sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.15.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vitejs/plugin-react@4.2.1(vite@5.0.12): + resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.6) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.0 + vite: 5.0.12(@types/node@20.10.5) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitest/expect@1.1.0: + resolution: {integrity: sha512-9IE2WWkcJo2BR9eqtY5MIo3TPmS50Pnwpm66A6neb2hvk/QSLfPXBz2qdiwUOQkwyFuuXEUj5380CbwfzW4+/w==} + dependencies: + '@vitest/spy': 1.1.0 + '@vitest/utils': 1.1.0 + chai: 4.3.10 + dev: true + + /@vitest/runner@1.1.0: + resolution: {integrity: sha512-zdNLJ00pm5z/uhbWF6aeIJCGMSyTyWImy3Fcp9piRGvueERFlQFbUwCpzVce79OLm2UHk9iwaMSOaU9jVHgNVw==} + dependencies: + '@vitest/utils': 1.1.0 + p-limit: 5.0.0 + pathe: 1.1.1 + dev: true + + /@vitest/snapshot@1.1.0: + resolution: {integrity: sha512-5O/wyZg09V5qmNmAlUgCBqflvn2ylgsWJRRuPrnHEfDNT6tQpQ8O1isNGgo+VxofISHqz961SG3iVvt3SPK/QQ==} + dependencies: + magic-string: 0.30.5 + pathe: 1.1.1 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@1.1.0: + resolution: {integrity: sha512-sNOVSU/GE+7+P76qYo+VXdXhXffzWZcYIPQfmkiRxaNCSPiLANvQx5Mx6ZURJ/ndtEkUJEpvKLXqAYTKEY+lTg==} + dependencies: + tinyspy: 2.2.0 + dev: true + + /@vitest/utils@1.1.0: + resolution: {integrity: sha512-z+s510fKmYz4Y41XhNs3vcuFTFhcij2YF7F8VQfMEYAAUfqQh0Zfg7+w9xdgFGhPf3tX3TicAe+8BDITk6ampQ==} + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + + /@xobotyi/scrollbar-width@1.9.5: + resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} + dev: false + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn-walk@8.3.1: + resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + + /agent-base@7.1.0: + resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} + engines: {node: '>= 14'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + + /array-includes@3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + is-string: 1.0.7 + dev: true + + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.findlastindex@1.2.3: + resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.1 + dev: true + + /array.prototype.flat@1.3.1: + resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.tosorted@1.1.2: + resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.1 + dev: true + + /arraybuffer.prototype.slice@1.0.1: + resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.0 + get-intrinsic: 1.2.1 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + dev: true + + /async@3.2.4: + resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + dev: true + + /asynciterator.prototype@1.0.0: + resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} + dependencies: + has-symbols: 1.0.3 + dev: true + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + /at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + dev: true + + /autoprefixer@10.4.16(postcss@8.4.32): + resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: '>=8.4.31' + dependencies: + browserslist: 4.21.10 + caniuse-lite: 1.0.30001571 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.32 + postcss-value-parser: 4.2.0 + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /axe-core@4.7.0: + resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} + engines: {node: '>=4'} + dev: true + + /axobject-query@3.2.1: + resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + dependencies: + dequal: 2.0.3 + dev: true + + /babel-plugin-polyfill-corejs2@0.4.7(@babel/core@7.23.6): + resolution: {integrity: sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.6 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.6) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-corejs3@0.8.7(@babel/core@7.23.6): + resolution: {integrity: sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.6) + core-js-compat: 3.34.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-polyfill-regenerator@0.5.4(@babel/core@7.23.6): + resolution: {integrity: sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/core': 7.23.6 + '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.6) + transitivePeerDependencies: + - supports-color + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: false + + /bplist-parser@0.2.0: + resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} + engines: {node: '>= 5.10.0'} + dependencies: + big-integer: 1.6.52 + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + + /browserslist@4.21.10: + resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001571 + electron-to-chromium: 1.4.508 + node-releases: 2.0.13 + update-browserslist-db: 1.0.11(browserslist@4.21.10) + dev: true + + /browserslist@4.22.2: + resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001571 + electron-to-chromium: 1.4.616 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.22.2) + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} + dependencies: + run-applescript: 5.0.0 + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.1 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + + /caniuse-lite@1.0.30001571: + resolution: {integrity: sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==} + + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + dev: false + + /cheerio@1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.1.0 + htmlparser2: 8.0.2 + parse5: 7.1.2 + parse5-htmlparser2-tree-adapter: 7.0.0 + dev: false + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + /classnames@2.3.2: + resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} + dev: false + + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: true + + /color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: true + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + dev: true + + /common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: false + + /copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + dependencies: + toggle-selection: 1.0.6 + dev: false + + /core-js-compat@3.32.1: + resolution: {integrity: sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA==} + dependencies: + browserslist: 4.21.10 + dev: true + + /core-js-compat@3.34.0: + resolution: {integrity: sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==} + dependencies: + browserslist: 4.22.2 + dev: true + + /core-js@3.34.0: + resolution: {integrity: sha512-aDdvlDder8QmY91H88GzNi9EtQi2TjvQhpCX6B1v/dAZHU1AuLgHvRh54RiOerpEhEW46Tkf+vgAViB/CWC0ag==} + requiresBuild: true + dev: false + + /cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + dev: false + + /crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + dev: true + + /css-in-js-utils@3.1.0: + resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} + dependencies: + hyphenate-style-name: 1.0.4 + dev: false + + /css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + dev: false + + /css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + dev: false + + /css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: false + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /cssstyle@3.0.0: + resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==} + engines: {node: '>=14'} + dependencies: + rrweb-cssom: 0.6.0 + dev: true + + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + + /damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + dev: true + + /data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + dev: true + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /default-browser-id@3.0.0: + resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} + engines: {node: '>=12'} + dependencies: + bplist-parser: 0.2.0 + untildify: 4.0.0 + dev: true + + /default-browser@4.0.0: + resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} + engines: {node: '>=14.16'} + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 7.2.0 + titleize: 3.0.0 + dev: true + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + gopd: 1.0.1 + has-property-descriptors: 1.0.0 + dev: true + + /define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: true + + /define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + dev: true + + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: true + + /destr@2.0.1: + resolution: {integrity: sha512-M1Ob1zPSIvlARiJUkKqvAZ3VAqQY6Jcuth/pBKQ2b1dX/Qx0OnJ8Vux6J2H5PTMQeRzWrrbTu70VxBfv/OPDJA==} + dev: false + + /detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + dev: false + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + dev: false + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: false + + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /dompurify@3.0.6: + resolution: {integrity: sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==} + dev: false + + /domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dev: false + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + + /ejs@3.1.9: + resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.8.7 + dev: true + + /electron-to-chromium@1.4.508: + resolution: {integrity: sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==} + dev: true + + /electron-to-chromium@1.4.616: + resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==} + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + + /enhanced-resolve@5.15.0: + resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + /error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + dependencies: + stackframe: 1.3.4 + dev: false + + /es-abstract@1.22.1: + resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.1 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.1 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.0 + safe-array-concat: 1.0.0 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.11 + dev: true + + /es-iterator-helpers@1.0.15: + resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + dependencies: + asynciterator.prototype: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.1 + es-abstract: 1.22.1 + es-set-tostringtag: 2.0.1 + function-bind: 1.1.1 + get-intrinsic: 1.2.1 + globalthis: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + iterator.prototype: 1.1.2 + safe-array-concat: 1.0.1 + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.19.10: + resolution: {integrity: sha512-S1Y27QGt/snkNYrRcswgRFqZjaTG5a5xM3EQo97uNBnH505pdzSNe/HLBq1v0RO7iK/ngdbhJB6mDAp0OK+iUA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.10 + '@esbuild/android-arm': 0.19.10 + '@esbuild/android-arm64': 0.19.10 + '@esbuild/android-x64': 0.19.10 + '@esbuild/darwin-arm64': 0.19.10 + '@esbuild/darwin-x64': 0.19.10 + '@esbuild/freebsd-arm64': 0.19.10 + '@esbuild/freebsd-x64': 0.19.10 + '@esbuild/linux-arm': 0.19.10 + '@esbuild/linux-arm64': 0.19.10 + '@esbuild/linux-ia32': 0.19.10 + '@esbuild/linux-loong64': 0.19.10 + '@esbuild/linux-mips64el': 0.19.10 + '@esbuild/linux-ppc64': 0.19.10 + '@esbuild/linux-riscv64': 0.19.10 + '@esbuild/linux-s390x': 0.19.10 + '@esbuild/linux-x64': 0.19.10 + '@esbuild/netbsd-x64': 0.19.10 + '@esbuild/openbsd-x64': 0.19.10 + '@esbuild/sunos-x64': 0.19.10 + '@esbuild/win32-arm64': 0.19.10 + '@esbuild/win32-ia32': 0.19.10 + '@esbuild/win32-x64': 0.19.10 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1)(eslint@8.56.0): + resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.2 + dependencies: + confusing-browser-globals: 1.0.11 + eslint: 8.56.0 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + object.assign: 4.1.4 + object.entries: 1.1.7 + semver: 6.3.1 + dev: true + + /eslint-config-airbnb@19.0.4(eslint-plugin-import@2.29.1)(eslint-plugin-jsx-a11y@6.8.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.33.2)(eslint@8.56.0): + resolution: {integrity: sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==} + engines: {node: ^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.3 + eslint-plugin-jsx-a11y: ^6.5.1 + eslint-plugin-react: ^7.28.0 + eslint-plugin-react-hooks: ^4.3.0 + dependencies: + eslint: 8.56.0 + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0) + eslint-plugin-react: 7.33.2(eslint@8.56.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0) + object.assign: 4.1.4 + object.entries: 1.1.7 + dev: true + + /eslint-config-prettier@9.1.0(eslint@8.56.0): + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.4 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.15.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0): + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + dependencies: + debug: 4.3.4 + enhanced-resolve: 5.15.0 + eslint: 8.56.0 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + fast-glob: 3.3.1 + get-tsconfig: 4.7.2 + is-core-module: 2.13.0 + is-glob: 4.0.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + debug: 3.2.7 + eslint: 8.56.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.15.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.3 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.56.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + hasown: 2.0.0 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-jsx-a11y@6.8.0(eslint@8.56.0): + resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + '@babel/runtime': 7.23.6 + aria-query: 5.3.0 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + ast-types-flow: 0.0.8 + axe-core: 4.7.0 + axobject-query: 3.2.1 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + es-iterator-helpers: 1.0.15 + eslint: 8.56.0 + hasown: 2.0.0 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + dev: true + + /eslint-plugin-prettier@5.1.1(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.1.1): + resolution: {integrity: sha512-WQpV3mSmIobb77s4qiCZu3dBrZZ0rj8ckSfBtRrgNK9Wnh2s3eiaxNTWloz1LJ1WtvqZES/PAI7PLvsrGt/CEA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.56.0 + eslint-config-prettier: 9.1.0(eslint@8.56.0) + prettier: 3.1.1 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.6 + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.56.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-plugin-react@7.33.2(eslint@8.56.0): + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + array.prototype.tosorted: 1.1.2 + doctrine: 2.1.0 + es-iterator-helpers: 1.0.15 + eslint: 8.56.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.1 + string.prototype.matchall: 4.0.9 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.56.0: + resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/regexpp': 4.8.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.56.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.21.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-walker@1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.2.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.2.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fast-loops@1.1.3: + resolution: {integrity: sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==} + dev: false + + /fast-shallow-equal@1.0.0: + resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} + dev: false + + /fastest-stable-stringify@2.0.2: + resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} + dev: false + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.0 + dev: true + + /filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + dependencies: + minimatch: 5.1.6 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flag-icons@7.1.0: + resolution: {integrity: sha512-AH4v++19bpC5P3Wh767top4wylJYJCWkFnvNiDqGHDxqSqdMZ49jpLXp8PWBHTTXaNQ+/A+QPrOwyiIGaiIhmw==} + dev: false + + /flat-cache@3.1.0: + resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.7 + keyv: 4.5.3 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /focus-trap-react@10.2.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-YXBpFu/hIeSu6NnmV2xlXzOYxuWkoOtar9jzgp3lOmjWLWY59C/b8DtDHEAV4SPU07Nd/t+nS/SBNGkhUBFmEw==} + peerDependencies: + prop-types: ^15.8.1 + react: '>=16.3.0' + react-dom: '>=16.3.0' + dependencies: + focus-trap: 7.5.4 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tabbable: 6.2.0 + dev: false + + /focus-trap@7.5.4: + resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} + dependencies: + tabbable: 6.2.0 + dev: false + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + dev: true + + /fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fscreen@1.2.0: + resolution: {integrity: sha512-hlq4+BU0hlPmwsFjwGGzZ+OZ9N/wq9Ljg/sq3pX+2CD7hrJsX9tJgWWK/wiNTFM212CLHWhicOoqwXyZGGetJg==} + dev: false + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /fuse.js@7.0.0: + resolution: {integrity: sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==} + engines: {node: '>=10'} + dev: false + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + + /get-func-name@3.0.0: + resolution: {integrity: sha512-6lB4zp64YzgT5KVoAuY0vBXQXNObRmelzfVCpx2dHkGVskX8WwjxTVd/kGUsVzxuOpSEF9BcD54ChSKMVjSsfQ==} + engines: {node: '>= 12'} + dev: true + + /get-intrinsic@1.2.1: + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-proto: 1.0.1 + has-symbols: 1.0.3 + dev: true + + /get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + dev: true + + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.3 + path-scurry: 1.10.1 + dev: true + + /glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + /globals@13.21.0: + resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.17.4 + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: true + + /hls.js@1.4.14: + resolution: {integrity: sha512-UppQjyvPVclg+6t2KY/Rv03h0+bA5u6zwqVoz4LAC/L0fgYmIaCD7ZCrwe8WI1Gv01be1XL0QFsRbSdIHV/Wbw==} + dev: false + + /hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + dev: false + + /html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + dependencies: + whatwg-encoding: 3.1.1 + dev: true + + /html-parse-stringify@3.0.1: + resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + dependencies: + void-elements: 3.1.0 + dev: false + + /htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + dev: false + + /http-proxy-agent@7.0.0: + resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /https-proxy-agent@7.0.2: + resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /hyphenate-style-name@1.0.4: + resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} + dev: false + + /i18next@23.7.11: + resolution: {integrity: sha512-A/vOkw8vY99YHU9A1Td3I1dcTiYaPnwBWzrpVzfXUXSYgogK3cmBcmop/0cnXPc6QpUWIyqaugKNxRUEZVk9Nw==} + dependencies: + '@babel/runtime': 7.23.6 + dev: false + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /immer@10.0.3: + resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==} + dev: false + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /inline-style-prefixer@7.0.0: + resolution: {integrity: sha512-I7GEdScunP1dQ6IM2mQWh6v0mOYdYmH3Bp31UecKdrcUgcURTcctSe1IECdUznSHKSmsHtjrT3CwCPI1pyxfUQ==} + dependencies: + css-in-js-utils: 3.1.0 + fast-loops: 1.1.3 + dev: false + + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 + dev: true + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: true + + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: true + + /is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + + /is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + dependencies: + is-docker: 3.0.0 + dev: true + + /is-map@2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true + + /is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + /is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + dev: true + + /is-set@2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.11 + dev: true + + /is-weakmap@2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-weakset@2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + dev: true + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /iso-639-1@3.1.0: + resolution: {integrity: sha512-rWcHp9dcNbxa5C8jA/cxFlWNFNwy5Vup0KcFvgA8sPQs9ZeJHj/Eq0Y8Yz2eL8XlWYpxw4iwh9FfTeVxyqdRMw==} + engines: {node: '>=6.0'} + dev: false + + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.4 + set-function-name: 2.0.1 + dev: true + + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /jake@10.8.7: + resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} + engines: {node: '>=10'} + hasBin: true + dependencies: + async: 3.2.4 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: true + + /jest-worker@26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 20.10.5 + merge-stream: 2.0.0 + supports-color: 7.2.0 + dev: true + + /jiti@1.19.3: + resolution: {integrity: sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==} + hasBin: true + dev: true + + /js-cookie@2.2.1: + resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} + dev: false + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsdom@23.0.1: + resolution: {integrity: sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + cssstyle: 3.0.0 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.7 + parse5: 7.1.2 + rrweb-cssom: 0.6.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.3 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + ws: 8.15.1 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + dev: true + + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.7 + array.prototype.flat: 1.3.1 + object.assign: 4.1.4 + object.values: 1.1.7 + dev: true + + /just-unique@4.2.0: + resolution: {integrity: sha512-cxQGGUiit6CGUpuuiezY8N4m1wgF4o7127rXEXDFcxeDUFfdV7gSkwA26Fe2wWBiNQq2SZOgN4gSmMxB/StA8Q==} + dev: true + + /jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + dev: false + + /keyv@4.5.3: + resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: false + + /language-subtag-registry@0.3.22: + resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} + dev: true + + /language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + dependencies: + language-subtag-registry: 0.3.22 + dev: true + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.4.2 + pkg-types: 1.0.3 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.compact@3.0.1: + resolution: {integrity: sha512-2ozeiPi+5eBXW1CLtzjk8XQFhQOEMwwfxblqeq6EGyTxZJ1bPATqilY0e6g2SLQpP4KuMeuioBhEnWz5Pr7ICQ==} + dev: false + + /lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: true + + /lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + dev: false + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + dev: true + + /lodash.pick@4.4.0: + resolution: {integrity: sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==} + dev: true + + /lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + + /loupe@2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + deprecated: Please upgrade to 2.3.7 which fixes GHSA-4q6p-r6v2-jvc5 + dependencies: + get-func-name: 3.0.0 + dev: true + + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /lru-cache@10.0.1: + resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} + engines: {node: 14 || >=16.14} + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + + /magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + dev: false + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /million@2.6.4: + resolution: {integrity: sha512-voUkdd/jHWrG+7NS+mX49Pat+POKdgGW78V7pYMSrTaOjUitR6ySEcAci8hn17Rsx1IMI3+5w41dkADM1J1ZEg==} + hasBin: true + dependencies: + '@babel/core': 7.23.6 + '@babel/generator': 7.23.6 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.6) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6) + '@babel/types': 7.23.6 + kleur: 4.1.5 + rollup: /@rollup/wasm-node@4.12.0 + unplugin: 1.5.1 + transitivePeerDependencies: + - supports-color + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /minipass@7.0.3: + resolution: {integrity: sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + dependencies: + acorn: 8.10.0 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.3.0 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + + /nano-css@5.6.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==} + peerDependencies: + react: '*' + react-dom: '*' + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + css-tree: 1.1.3 + csstype: 3.1.2 + fastest-stable-stringify: 2.0.2 + inline-style-prefixer: 7.0.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + rtl-css-js: 1.16.1 + stacktrace-js: 2.0.2 + stylis: 4.3.0 + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /nanoid@5.0.3: + resolution: {integrity: sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==} + engines: {node: ^18 || >=20} + hasBin: true + dev: false + + /nanoid@5.0.4: + resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==} + engines: {node: ^18 || >=20} + hasBin: true + dev: false + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + dev: true + + /node-fetch-native@1.4.0: + resolution: {integrity: sha512-F5kfEj95kX8tkDhUCYdV8dg3/8Olx/94zB8+ZNthFs6Bz31UpUi8Xh40TN3thLwXgrwXry1pEg9lJ++tLWTcqA==} + dev: false + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + + /node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + dev: false + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /npm-run-path@5.2.0: + resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: false + + /nwsapi@2.2.7: + resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /object.groupby@1.0.1: + resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + dev: true + + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + dependencies: + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /ofetch@1.3.3: + resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==} + dependencies: + destr: 2.0.1 + node-fetch-native: 1.4.0 + ufo: 1.3.0 + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + + /open@9.1.0: + resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} + engines: {node: '>=14.16'} + dependencies: + default-browser: 4.0.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 2.2.0 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse5-htmlparser2-tree-adapter@7.0.0: + resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + dependencies: + domhandler: 5.0.3 + parse5: 7.1.2 + dev: false + + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.0.1 + minipass: 7.0.3 + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.2 + pathe: 1.1.1 + dev: true + + /postcss-import@15.1.0(postcss@8.4.32): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: '>=8.4.31' + dependencies: + postcss: 8.4.32 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.4 + dev: true + + /postcss-js@4.0.1(postcss@8.4.32): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: '>=8.4.31' + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.32 + dev: true + + /postcss-load-config@4.0.1(postcss@8.4.32): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.4.31' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.32 + yaml: 2.3.2 + dev: true + + /postcss-nested@6.0.1(postcss@8.4.32): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: '>=8.4.31' + dependencies: + postcss: 8.4.32 + postcss-selector-parser: 6.0.13 + dev: true + + /postcss-rtl@2.0.0(postcss@8.4.32): + resolution: {integrity: sha512-vFu78CvaGY9BafWRHNgDm6OjUxzRCWWCrp+KtnyXdgwibLwb/j5ls8Z/ubvOsk9B/Q2NLwSPrXRARKMaa9RBmA==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: '>=8.4.31' + dependencies: + postcss: 8.4.32 + rtlcss: 4.0.0 + dev: true + + /postcss-rtlcss@4.0.9(postcss@8.4.32): + 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.32 + rtlcss: 4.1.1 + dev: true + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss@8.4.32: + resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier-plugin-tailwindcss@0.5.9(prettier@3.1.1): + resolution: {integrity: sha512-9x3t1s2Cjbut2QiP+O0mDqV3gLXTe2CgRlQDgucopVkUdw26sQi53p/q4qvGxMLBDfk/dcTV57Aa/zYwz9l8Ew==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + prettier-plugin-twig-melody: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier-plugin-twig-melody: + optional: true + dependencies: + prettier: 3.1.1 + dev: true + + /prettier@3.1.1: + resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + dev: true + + /pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + /psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: true + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + dev: false + + /react-ga4@2.1.0: + resolution: {integrity: sha512-ZKS7PGNFqqMd3PJ6+C2Jtz/o1iU9ggiy8Y8nUeksgVuvNISbmrQtJiZNvC/TjDsqD0QlU5Wkgs7i+w9+OjHhhQ==} + dev: false + + /react-google-recaptcha-v3@1.10.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-K3AYzSE0SasTn+XvV2tq+6YaxM+zQypk9rbCgG4OVUt7Rh4ze9basIKefoBz9sC0CNslJj9N1uwTTgRMJQbQJQ==} + peerDependencies: + react: ^16.3 || ^17.0 || ^18.0 + react-dom: ^17.0 || ^18.0 + dependencies: + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-helmet-async@2.0.4(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yxjQMWposw+akRfvpl5+8xejl4JtUlHnEBcji6u8/e6oc7ozT+P9PNTWMhCbz2y9tc5zPegw2BvKjQA+NwdEjQ==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 + dependencies: + invariant: 2.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-fast-compare: 3.2.2 + shallowequal: 1.1.0 + dev: false + + /react-i18next@14.0.0(i18next@23.7.11)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OCrS8rHNAmnr8ggGRDxjakzihrMW7HCbsplduTm3EuuQ6fyvWGT41ksZpqbduYoqJurBmEsEVZ1pILSUWkHZng==} + peerDependencies: + i18next: '>= 23.2.3' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.22.11 + html-parse-stringify: 3.0.1 + i18next: 23.7.11 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /react-lazy-with-preload@2.2.1: + resolution: {integrity: sha512-ONSb8gizLE5jFpdHAclZ6EAAKuFX2JydnFXPPPjoUImZlLjGtKzyBS8SJgJq7CpLgsGKh9QCZdugJyEEOVC16Q==} + dev: false + + /react-refresh@0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: true + + /react-router-dom@6.21.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + '@remix-run/router': 1.14.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router: 6.21.1(react@18.2.0) + dev: false + + /react-router@6.21.1(react@18.2.0): + resolution: {integrity: sha512-W0l13YlMTm1YrpVIOpjCADJqEUpz1vm+CMo47RuFX4Ftegwm6KOYsL5G3eiE52jnJpKvzm6uB/vTKTPKM8dmkA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + dependencies: + '@remix-run/router': 1.14.1 + react: 18.2.0 + dev: false + + /react-sticky-el@2.1.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-oo+a2GedF4QMfCfm20e9gD+RuuQp/ngvwGMUXAXpST+h4WnmKhuv7x6MQ4X/e3AHiLYgE0zDyJo1Pzo8m51KpA==} + peerDependencies: + react: '>=16.3.0' + react-dom: '>=16.3.0' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-turnstile@1.1.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wfhSf4JtXlmLRkfxMryU8yEeCbh401muKoInhx+TegYwP8RprUW5XPZa8WnCNZiYpMy1i6IXAb1Ar7xj5HxJag==} + peerDependencies: + react: '>= 17.0.0' + react-dom: '>= 17.0.0' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-universal-interface@0.6.2(react@18.2.0)(tslib@2.6.2): + resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} + peerDependencies: + react: '*' + tslib: '*' + dependencies: + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /react-use@17.4.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-1jPtmWLD8OJJNYCdYLJEH/HM+bPDfJuyGwCYeJFgPmWY8ttwpgZnW5QnzgM55CYUByUiTjHxsGOnEpLl6yQaoQ==} + peerDependencies: + react: '*' + react-dom: '*' + dependencies: + '@types/js-cookie': 2.2.7 + '@xobotyi/scrollbar-width': 1.9.5 + copy-to-clipboard: 3.3.3 + fast-deep-equal: 3.1.3 + fast-shallow-equal: 1.0.0 + js-cookie: 2.2.1 + nano-css: 5.6.1(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-universal-interface: 0.6.2(react@18.2.0)(tslib@2.6.2) + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + set-harmonic-interval: 1.0.1 + throttle-debounce: 3.0.1 + ts-easing: 0.2.0 + tslib: 2.6.2 + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + + /reflect.getprototypeof@1.0.4: + resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.1 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 + dev: true + + /regenerate-unicode-properties@10.1.0: + resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} + engines: {node: '>=4'} + dependencies: + regenerate: 1.4.2 + dev: true + + /regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + dev: true + + /regenerator-runtime@0.14.0: + resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + + /regenerator-transform@0.15.2: + resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + dependencies: + '@babel/runtime': 7.22.11 + dev: true + + /regexp.prototype.flags@1.5.0: + resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /regexpu-core@5.3.2: + resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} + engines: {node: '>=4'} + dependencies: + '@babel/regjsgen': 0.8.0 + regenerate: 1.4.2 + regenerate-unicode-properties: 10.1.0 + regjsparser: 0.9.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.1.0 + dev: true + + /regjsparser@0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: true + + /resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + dev: false + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /resolve@1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve@2.0.0-next.4: + resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.12.0): + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser + peerDependencies: + rollup: npm:@rollup/wasm-node + dependencies: + '@babel/code-frame': 7.23.5 + jest-worker: 26.6.2 + rollup: /@rollup/wasm-node@4.12.0 + serialize-javascript: 4.0.0 + terser: 5.19.3 + dev: true + + /rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.12.0): + resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + rollup: npm:@rollup/wasm-node + peerDependenciesMeta: + rollup: + optional: true + dependencies: + open: 8.4.2 + picomatch: 2.3.1 + rollup: /@rollup/wasm-node@4.12.0 + source-map: 0.7.4 + yargs: 17.7.2 + dev: true + + /rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + dev: true + + /rtl-css-js@1.16.1: + resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + dependencies: + '@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.32 + 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.32 + strip-json-comments: 3.1.1 + dev: true + + /run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + dependencies: + execa: 5.1.1 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-array-concat@1.0.0: + resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-regex: 1.1.4 + dev: true + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + dev: false + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + + /serialize-javascript@4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + dependencies: + randombytes: 2.1.0 + dev: true + + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: false + + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.0 + dev: true + + /set-harmonic-interval@1.0.1: + resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} + engines: {node: '>=6.9'} + dev: false + + /shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + object-inspect: 1.12.3 + dev: true + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slugify@1.6.6: + resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} + engines: {node: '>=8.0.0'} + dev: false + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + dev: false + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + + /source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + dependencies: + whatwg-url: 7.1.0 + dev: true + + /sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: true + + /stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + dependencies: + stackframe: 1.3.4 + dev: false + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + dev: false + + /stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + dev: false + + /stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + dev: false + + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /string.prototype.matchall@4.0.9: + resolution: {integrity: sha512-6i5hL3MqG/K2G43mWXWgP+qizFW/QH/7kCNN13JrJS5q48FN5IKksLDscexKP3dnmB6cdm9jlNgAsWNLpSykmA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + regexp.prototype.flags: 1.5.0 + side-channel: 1.0.4 + dev: true + + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.10.0 + dev: true + + /stylis@4.3.0: + resolution: {integrity: sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==} + dev: false + + /subsrt-ts@2.1.2: + resolution: {integrity: sha512-45yNlK42Z0pz4lAaNYbR5P60M2jmHl+gfAaiJxDIXsXXqoE7TkDCzl/00HgWyZXKkdIU6s8FiNtRvrlOZb+5Qg==} + hasBin: true + dev: false + + /sucrase@3.34.0: + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} + engines: {node: '>=8'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + + /synckit@0.8.6: + resolution: {integrity: sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/utils': 2.4.2 + tslib: 2.6.2 + dev: true + + /tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + dev: false + + /tailwind-scrollbar@3.0.5(tailwindcss@3.4.0): + resolution: {integrity: sha512-0ZwxTivevqq9BY9fRP9zDjHl7Tu+J5giBGbln+0O1R/7nHtBUKnjQcA1aTIhK7Oyjp6Uc/Dj6/dn8Dq58k5Uww==} + engines: {node: '>=12.13.0'} + peerDependencies: + tailwindcss: 3.x + dependencies: + tailwindcss: 3.4.0 + dev: true + + /tailwindcss-themer@4.0.0(tailwindcss@3.4.0): + resolution: {integrity: sha512-kngKDW+z36JtjvaNziX0aesO95r8f5+3FFdANVFDt1uzNHZsL6XLla1YDK6EBAYFqPichoPDbO4lrrTyB+UAbA==} + peerDependencies: + tailwindcss: ^3.1.0 + dependencies: + color: 4.2.3 + just-unique: 4.2.0 + lodash.merge: 4.6.2 + lodash.mergewith: 4.6.2 + tailwindcss: 3.4.0 + dev: true + + /tailwindcss@3.4.0: + resolution: {integrity: sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.1 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.19.3 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.32 + postcss-import: 15.1.0(postcss@8.4.32) + postcss-js: 4.0.1(postcss@8.4.32) + postcss-load-config: 4.0.1(postcss@8.4.32) + postcss-nested: 6.0.1(postcss@8.4.32) + postcss-selector-parser: 6.0.13 + resolve: 1.22.4 + sucrase: 3.34.0 + transitivePeerDependencies: + - ts-node + dev: true + + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + + /temp-dir@2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + dev: true + + /tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + dev: true + + /terser@5.19.3: + resolution: {integrity: sha512-pQzJ9UJzM0IgmT4FAtYI6+VqFf0lj/to58AV0Xfgg0Up37RyPG7Al+1cepC6/BVuAxR9oNb41/DL4DEoHJvTdg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.5 + acorn: 8.11.2 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true + + /throttle-debounce@3.0.1: + resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} + engines: {node: '>=10'} + dev: false + + /tiny-invariant@1.3.1: + resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} + dev: true + + /tinybench@2.5.1: + resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + dev: true + + /tinypool@0.8.1: + resolution: {integrity: sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} + engines: {node: '>=14.0.0'} + dev: true + + /titleize@3.0.0: + resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} + engines: {node: '>=12'} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + + /toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + dev: false + + /tough-cookie@4.1.3: + resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.3.0 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + /tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + dependencies: + punycode: 2.3.1 + dev: true + + /tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + dependencies: + punycode: 2.3.1 + dev: true + + /ts-api-utils@1.0.3(typescript@5.3.3): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.3.3 + dev: true + + /ts-easing@0.2.0: + resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} + dev: false + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true + + /tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /type-fest@4.8.3: + resolution: {integrity: sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==} + engines: {node: '>=16'} + dev: true + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: true + + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: false + + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /ufo@1.3.0: + resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==} + + /uglify-js@3.17.4: + resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} + engines: {node: '>=0.8.0'} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + /unicode-canonical-property-names-ecmascript@2.0.0: + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} + dev: true + + /unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-property-aliases-ecmascript: 2.1.0 + dev: true + + /unicode-match-property-value-ecmascript@2.1.0: + resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + engines: {node: '>=4'} + dev: true + + /unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + dev: true + + /unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + dependencies: + crypto-random-string: 2.0.0 + dev: true + + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + + /universalify@2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + + /unpacker@1.0.1: + resolution: {integrity: sha512-0HTljwp8+JBdITpoHcK1LWi7X9U2BspUmWv78UWZh7NshYhbh1nec8baY/iSbe2OQTZ2bhAtVdnr6/BTD0DKVg==} + dev: false + + /unplugin@1.5.1: + resolution: {integrity: sha512-0QkvG13z6RD+1L1FoibQqnvTwVBXvS4XSPwAyinVgoOCl2jAgwzdUKmEj05o4Lt8xwQI85Hb6mSyYkcAGwZPew==} + dependencies: + acorn: 8.11.2 + chokidar: 3.5.3 + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.6.1 + dev: false + + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true + + /upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + dev: true + + /update-browserslist-db@1.0.11(browserslist@4.21.10): + resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.10 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.22.2): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.2 + escalade: 3.1.1 + picocolors: 1.0.0 + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: true + + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /vite-node@1.1.0(@types/node@20.10.5): + resolution: {integrity: sha512-jV48DDUxGLEBdHCQvxL1mEh7+naVy+nhUUUaPAZLd3FJgXuxQiewHcfeZebbJ6onDqNGkP4r3MhQ342PRlG81Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 5.0.12(@types/node@20.10.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite-plugin-checker@0.6.2(eslint@8.56.0)(typescript@5.3.3)(vite@5.0.12): + resolution: {integrity: sha512-YvvvQ+IjY09BX7Ab+1pjxkELQsBd4rPhWNw8WLBeFVxu/E7O+n6VYAqNsKdK/a2luFlX/sMpoWdGFfg4HvwdJQ==} + engines: {node: '>=14.16'} + peerDependencies: + eslint: '>=7' + meow: ^9.0.0 + optionator: ^0.9.1 + stylelint: '>=13' + typescript: '*' + vite: '>=2.0.0' + vls: '*' + vti: '*' + vue-tsc: '>=1.3.9' + peerDependenciesMeta: + eslint: + optional: true + meow: + optional: true + optionator: + optional: true + stylelint: + optional: true + typescript: + optional: true + vls: + optional: true + vti: + optional: true + vue-tsc: + optional: true + dependencies: + '@babel/code-frame': 7.22.13 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + chokidar: 3.5.3 + commander: 8.3.0 + eslint: 8.56.0 + fast-glob: 3.3.1 + fs-extra: 11.1.1 + lodash.debounce: 4.0.8 + lodash.pick: 4.4.0 + npm-run-path: 4.0.1 + semver: 7.5.4 + strip-ansi: 6.0.1 + tiny-invariant: 1.3.1 + typescript: 5.3.3 + vite: 5.0.12(@types/node@20.10.5) + vscode-languageclient: 7.0.0 + vscode-languageserver: 7.0.0 + vscode-languageserver-textdocument: 1.0.8 + vscode-uri: 3.0.7 + dev: true + + /vite-plugin-package-version@1.1.0(vite@5.0.12): + resolution: {integrity: sha512-TPoFZXNanzcaKCIrC3e2L/TVRkkRLB6l4RPN/S7KbG7rWfyLcCEGsnXvxn6qR7fyZwXalnnSN/I9d6pSFjHpEA==} + peerDependencies: + vite: '>=2.0.0-beta.69' + dependencies: + vite: 5.0.12(@types/node@20.10.5) + dev: true + + /vite-plugin-pwa@0.17.4(vite@5.0.12)(workbox-build@7.0.0)(workbox-window@7.0.0): + resolution: {integrity: sha512-j9iiyinFOYyof4Zk3Q+DtmYyDVBDAi6PuMGNGq6uGI0pw7E+LNm9e+nQ2ep9obMP/kjdWwzilqUrlfVRj9OobA==} + engines: {node: '>=16.0.0'} + peerDependencies: + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 + workbox-build: ^7.0.0 + workbox-window: ^7.0.0 + dependencies: + debug: 4.3.4 + fast-glob: 3.3.2 + pretty-bytes: 6.1.1 + vite: 5.0.12(@types/node@20.10.5) + workbox-build: 7.0.0 + workbox-window: 7.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /vite-plugin-static-copy@1.0.0(vite@5.0.12): + resolution: {integrity: sha512-kMlrB3WDtC5GzFedNIPkpjnOAr8M11PfWOiUaONrUZ3AqogTsOmIhTt6w7Fh311wl8pN81ld7sfuOEogFJ9N8A==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + dependencies: + chokidar: 3.5.3 + fast-glob: 3.3.1 + fs-extra: 11.1.1 + picocolors: 1.0.0 + vite: 5.0.12(@types/node@20.10.5) + dev: true + + /vite@5.0.12(@types/node@20.10.5): + resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.10.5 + esbuild: 0.19.10 + postcss: 8.4.32 + rollup: /@rollup/wasm-node@4.12.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@1.1.0(@types/node@20.10.5)(jsdom@23.0.1): + resolution: {integrity: sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': ^1.0.0 + '@vitest/ui': ^1.0.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 20.10.5 + '@vitest/expect': 1.1.0 + '@vitest/runner': 1.1.0 + '@vitest/snapshot': 1.1.0 + '@vitest/spy': 1.1.0 + '@vitest/utils': 1.1.0 + acorn-walk: 8.3.1 + cac: 6.7.14 + chai: 4.3.10 + debug: 4.3.4 + execa: 8.0.1 + jsdom: 23.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.5 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.5.1 + tinypool: 0.8.1 + vite: 5.0.12(@types/node@20.10.5) + vite-node: 1.1.0(@types/node@20.10.5) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + dev: false + + /vscode-jsonrpc@6.0.0: + resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} + engines: {node: '>=8.0.0 || >=10.0.0'} + dev: true + + /vscode-languageclient@7.0.0: + resolution: {integrity: sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==} + engines: {vscode: ^1.52.0} + dependencies: + minimatch: 3.1.2 + semver: 7.5.4 + vscode-languageserver-protocol: 3.16.0 + dev: true + + /vscode-languageserver-protocol@3.16.0: + resolution: {integrity: sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==} + dependencies: + vscode-jsonrpc: 6.0.0 + vscode-languageserver-types: 3.16.0 + dev: true + + /vscode-languageserver-textdocument@1.0.8: + resolution: {integrity: sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==} + dev: true + + /vscode-languageserver-types@3.16.0: + resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==} + dev: true + + /vscode-languageserver@7.0.0: + resolution: {integrity: sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==} + hasBin: true + dependencies: + vscode-languageserver-protocol: 3.16.0 + dev: true + + /vscode-uri@3.0.7: + resolution: {integrity: sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==} + dev: true + + /w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + dependencies: + xml-name-validator: 5.0.0 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + + /webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + dev: false + + /webpack-virtual-modules@0.6.1: + resolution: {integrity: sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==} + dev: false + + /whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + dependencies: + iconv-lite: 0.6.3 + dev: true + + /whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + dev: true + + /whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + dev: true + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + + /whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.0 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.11 + dev: true + + /which-collection@1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + + /which-typed-array@1.1.11: + resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + + /wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + dev: true + + /workbox-background-sync@7.0.0: + resolution: {integrity: sha512-S+m1+84gjdueM+jIKZ+I0Lx0BDHkk5Nu6a3kTVxP4fdj3gKouRNmhO8H290ybnJTOPfBDtTMXSQA/QLTvr7PeA==} + dependencies: + idb: 7.1.1 + workbox-core: 7.0.0 + dev: true + + /workbox-broadcast-update@7.0.0: + resolution: {integrity: sha512-oUuh4jzZrLySOo0tC0WoKiSg90bVAcnE98uW7F8GFiSOXnhogfNDGZelPJa+6KpGBO5+Qelv04Hqx2UD+BJqNQ==} + dependencies: + workbox-core: 7.0.0 + dev: true + + /workbox-build@7.0.0: + resolution: {integrity: sha512-CttE7WCYW9sZC+nUYhQg3WzzGPr4IHmrPnjKiu3AMXsiNQKx+l4hHl63WTrnicLmKEKHScWDH8xsGBdrYgtBzg==} + engines: {node: '>=16.0.0'} + dependencies: + '@apideck/better-ajv-errors': 0.3.6(ajv@8.12.0) + '@babel/core': 7.23.6 + '@babel/preset-env': 7.23.6(@babel/core@7.23.6) + '@babel/runtime': 7.23.6 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.12.0) + '@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.12.0) + '@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.12.0) + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.12.0 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 7.2.3 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: /@rollup/wasm-node@4.12.0 + rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.12.0) + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 7.0.0 + workbox-broadcast-update: 7.0.0 + workbox-cacheable-response: 7.0.0 + workbox-core: 7.0.0 + workbox-expiration: 7.0.0 + workbox-google-analytics: 7.0.0 + workbox-navigation-preload: 7.0.0 + workbox-precaching: 7.0.0 + workbox-range-requests: 7.0.0 + workbox-recipes: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + workbox-streams: 7.0.0 + workbox-sw: 7.0.0 + workbox-window: 7.0.0 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + dev: true + + /workbox-cacheable-response@7.0.0: + resolution: {integrity: sha512-0lrtyGHn/LH8kKAJVOQfSu3/80WDc9Ma8ng0p2i/5HuUndGttH+mGMSvOskjOdFImLs2XZIimErp7tSOPmu/6g==} + dependencies: + workbox-core: 7.0.0 + dev: true + + /workbox-core@7.0.0: + resolution: {integrity: sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==} + dev: true + + /workbox-expiration@7.0.0: + resolution: {integrity: sha512-MLK+fogW+pC3IWU9SFE+FRStvDVutwJMR5if1g7oBJx3qwmO69BNoJQVaMXq41R0gg3MzxVfwOGKx3i9P6sOLQ==} + dependencies: + idb: 7.1.1 + workbox-core: 7.0.0 + dev: true + + /workbox-google-analytics@7.0.0: + resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + dependencies: + workbox-background-sync: 7.0.0 + workbox-core: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + dev: true + + /workbox-navigation-preload@7.0.0: + resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==} + dependencies: + workbox-core: 7.0.0 + dev: true + + /workbox-precaching@7.0.0: + resolution: {integrity: sha512-EC0vol623LJqTJo1mkhD9DZmMP604vHqni3EohhQVwhJlTgyKyOkMrZNy5/QHfOby+39xqC01gv4LjOm4HSfnA==} + dependencies: + workbox-core: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + dev: true + + /workbox-range-requests@7.0.0: + resolution: {integrity: sha512-SxAzoVl9j/zRU9OT5+IQs7pbJBOUOlriB8Gn9YMvi38BNZRbM+RvkujHMo8FOe9IWrqqwYgDFBfv6sk76I1yaQ==} + dependencies: + workbox-core: 7.0.0 + dev: true + + /workbox-recipes@7.0.0: + resolution: {integrity: sha512-DntcK9wuG3rYQOONWC0PejxYYIDHyWWZB/ueTbOUDQgefaeIj1kJ7pdP3LZV2lfrj8XXXBWt+JDRSw1lLLOnww==} + dependencies: + workbox-cacheable-response: 7.0.0 + workbox-core: 7.0.0 + workbox-expiration: 7.0.0 + workbox-precaching: 7.0.0 + workbox-routing: 7.0.0 + workbox-strategies: 7.0.0 + dev: true + + /workbox-routing@7.0.0: + resolution: {integrity: sha512-8YxLr3xvqidnbVeGyRGkaV4YdlKkn5qZ1LfEePW3dq+ydE73hUUJJuLmGEykW3fMX8x8mNdL0XrWgotcuZjIvA==} + dependencies: + workbox-core: 7.0.0 + dev: true + + /workbox-strategies@7.0.0: + resolution: {integrity: sha512-dg3qJU7tR/Gcd/XXOOo7x9QoCI9nk74JopaJaYAQ+ugLi57gPsXycVdBnYbayVj34m6Y8ppPwIuecrzkpBVwbA==} + dependencies: + workbox-core: 7.0.0 + dev: true + + /workbox-streams@7.0.0: + resolution: {integrity: sha512-moVsh+5to//l6IERWceYKGiftc+prNnqOp2sgALJJFbnNVpTXzKISlTIsrWY+ogMqt+x1oMazIdHj25kBSq/HQ==} + dependencies: + workbox-core: 7.0.0 + workbox-routing: 7.0.0 + dev: true + + /workbox-sw@7.0.0: + resolution: {integrity: sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==} + dev: true + + /workbox-window@7.0.0: + resolution: {integrity: sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==} + dependencies: + '@types/trusted-types': 2.0.3 + workbox-core: 7.0.0 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /ws@8.15.1: + resolution: {integrity: sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + /xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + dev: true + + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + /yaml@2.3.2: + resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==} + engines: {node: '>= 14'} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + + /zustand@4.4.7(@types/react@18.2.45)(immer@10.0.3)(react@18.2.0): + resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + dependencies: + '@types/react': 18.2.45 + immer: 10.0.3 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..33ad091d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/prettierrc.js b/prettierrc.js new file mode 100644 index 00000000..0af34d8e --- /dev/null +++ b/prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + trailingComma: "all", + singleQuote: true +}; diff --git a/public/_headers b/public/_headers new file mode 100644 index 00000000..0f4e3f29 --- /dev/null +++ b/public/_headers @@ -0,0 +1,13 @@ +/* + X-Frame-Options: DENY + X-XSS-Protection: 1; mode=block + X-Content-Type-Options: nosniff + Referrer-Policy: origin-when-cross-origin + Cache-Control: public, max-age=0, s-maxage=0, must-revalidate + +/manifest.webmanifest + Content-Type: application/manifest+json + +# assets get a long cache instead of no cache +/assets/* + Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 00000000..ee9870cf --- /dev/null +++ b/public/_redirects @@ -0,0 +1,2 @@ +/assets/* /assets/:splat 200 +/* /index.html 200 diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 00000000..f71a0514 Binary files /dev/null and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 00000000..b44f4951 Binary files /dev/null and b/public/android-chrome-512x512.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 00000000..f45b8fd4 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/browserconfig.xml b/public/browserconfig.xml new file mode 100644 index 00000000..1252a424 --- /dev/null +++ b/public/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #120f1d + + + diff --git a/public/config.js b/public/config.js new file mode 100644 index 00000000..a97473f1 --- /dev/null +++ b/public/config.js @@ -0,0 +1,19 @@ +window.__CONFIG__ = { + // The URL for the CORS proxy, the URL must NOT end with a slash! + VITE_CORS_PROXY_URL: "https://flixweb-proxy.netlify.app", + + // The READ API key to access TMDB + VITE_TMDB_READ_API_KEY: "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJiZmU0OGY4NjFkY2NmMjczMzUyMDdmMWVjYmVkNjNjNiIsInN1YiI6IjY1YjNmMWI0NTk0Yzk0MDE2MzNkZDBjNSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.GiCKswc2u9NraBbujm0ykI5G3p-K9WJoHg40jYbFv4o", + + // The DMCA email displayed in the footer, null to hide the DMCA link + VITE_DMCA_EMAIL: null, + + // Whether to disable hash-based routing, leave this as false if you don't know what this is + VITE_NORMAL_ROUTER: false, + + // The backend URL to communicate with, defaults to the movie-web hosted one at backend.movie-web.app + VITE_BACKEND_URL: null, + + // A comma separated list of disallowed IDs in the case of a DMCA claim - in the format "series-" and "movie-" + VITE_DISALLOWED_IDS: "" +}; diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 00000000..56d0a43b Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 00000000..ef644cbb Binary files /dev/null and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..b48e0702 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/flags/galicia.svg b/public/flags/galicia.svg new file mode 100644 index 00000000..2eb27952 --- /dev/null +++ b/public/flags/galicia.svg @@ -0,0 +1,199 @@ + + + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/flags/skull.svg b/public/flags/skull.svg new file mode 100644 index 00000000..02c4741b --- /dev/null +++ b/public/flags/skull.svg @@ -0,0 +1 @@ + diff --git a/public/flags/tokiPona.svg b/public/flags/tokiPona.svg new file mode 100644 index 00000000..d0bea782 --- /dev/null +++ b/public/flags/tokiPona.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + diff --git a/public/flixico.jpg b/public/flixico.jpg new file mode 100644 index 00000000..ef5127cc Binary files /dev/null and b/public/flixico.jpg differ diff --git a/public/lightbar-images/fishie.png b/public/lightbar-images/fishie.png new file mode 100644 index 00000000..8c528ba4 Binary files /dev/null and b/public/lightbar-images/fishie.png differ diff --git a/public/lightbar-images/santa.png b/public/lightbar-images/santa.png new file mode 100644 index 00000000..fd799ee2 Binary files /dev/null and b/public/lightbar-images/santa.png differ diff --git a/public/lightbar-images/snowflake.svg b/public/lightbar-images/snowflake.svg new file mode 100644 index 00000000..50d9c382 --- /dev/null +++ b/public/lightbar-images/snowflake.svg @@ -0,0 +1,45 @@ + + + + + + + + \ No newline at end of file diff --git a/public/mstile-150x150.png b/public/mstile-150x150.png new file mode 100644 index 00000000..a001008d Binary files /dev/null and b/public/mstile-150x150.png differ diff --git a/public/ping.txt b/public/ping.txt new file mode 100644 index 00000000..8e554694 --- /dev/null +++ b/public/ping.txt @@ -0,0 +1 @@ +pong diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/public/safari-pinned-tab.svg b/public/safari-pinned-tab.svg new file mode 100644 index 00000000..647fc295 --- /dev/null +++ b/public/safari-pinned-tab.svg @@ -0,0 +1,18 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/public/splash_screens/10.2__iPad_landscape.png b/public/splash_screens/10.2__iPad_landscape.png new file mode 100644 index 00000000..62d541fc Binary files /dev/null and b/public/splash_screens/10.2__iPad_landscape.png differ diff --git a/public/splash_screens/10.2__iPad_portrait.png b/public/splash_screens/10.2__iPad_portrait.png new file mode 100644 index 00000000..97c5cea9 Binary files /dev/null and b/public/splash_screens/10.2__iPad_portrait.png differ diff --git a/public/splash_screens/10.5__iPad_Air_landscape.png b/public/splash_screens/10.5__iPad_Air_landscape.png new file mode 100644 index 00000000..f8001cf3 Binary files /dev/null and b/public/splash_screens/10.5__iPad_Air_landscape.png differ diff --git a/public/splash_screens/10.5__iPad_Air_portrait.png b/public/splash_screens/10.5__iPad_Air_portrait.png new file mode 100644 index 00000000..c6750e7a Binary files /dev/null and b/public/splash_screens/10.5__iPad_Air_portrait.png differ diff --git a/public/splash_screens/10.9__iPad_Air_landscape.png b/public/splash_screens/10.9__iPad_Air_landscape.png new file mode 100644 index 00000000..03a44384 Binary files /dev/null and b/public/splash_screens/10.9__iPad_Air_landscape.png differ diff --git a/public/splash_screens/10.9__iPad_Air_portrait.png b/public/splash_screens/10.9__iPad_Air_portrait.png new file mode 100644 index 00000000..d7c068b2 Binary files /dev/null and b/public/splash_screens/10.9__iPad_Air_portrait.png differ diff --git a/public/splash_screens/11__iPad_Pro__10.5__iPad_Pro_landscape.png b/public/splash_screens/11__iPad_Pro__10.5__iPad_Pro_landscape.png new file mode 100644 index 00000000..c2e02978 Binary files /dev/null and b/public/splash_screens/11__iPad_Pro__10.5__iPad_Pro_landscape.png differ diff --git a/public/splash_screens/11__iPad_Pro__10.5__iPad_Pro_portrait.png b/public/splash_screens/11__iPad_Pro__10.5__iPad_Pro_portrait.png new file mode 100644 index 00000000..59178e3f Binary files /dev/null and b/public/splash_screens/11__iPad_Pro__10.5__iPad_Pro_portrait.png differ diff --git a/public/splash_screens/12.9__iPad_Pro_landscape.png b/public/splash_screens/12.9__iPad_Pro_landscape.png new file mode 100644 index 00000000..db66590f Binary files /dev/null and b/public/splash_screens/12.9__iPad_Pro_landscape.png differ diff --git a/public/splash_screens/12.9__iPad_Pro_portrait.png b/public/splash_screens/12.9__iPad_Pro_portrait.png new file mode 100644 index 00000000..2fc0f2a0 Binary files /dev/null and b/public/splash_screens/12.9__iPad_Pro_portrait.png differ diff --git a/public/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png b/public/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png new file mode 100644 index 00000000..c484d5b3 Binary files /dev/null and b/public/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png differ diff --git a/public/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png b/public/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png new file mode 100644 index 00000000..2b06bf2e Binary files /dev/null and b/public/splash_screens/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png differ diff --git a/public/splash_screens/8.3__iPad_Mini_landscape.png b/public/splash_screens/8.3__iPad_Mini_landscape.png new file mode 100644 index 00000000..d43bf7c1 Binary files /dev/null and b/public/splash_screens/8.3__iPad_Mini_landscape.png differ diff --git a/public/splash_screens/8.3__iPad_Mini_portrait.png b/public/splash_screens/8.3__iPad_Mini_portrait.png new file mode 100644 index 00000000..83eace82 Binary files /dev/null and b/public/splash_screens/8.3__iPad_Mini_portrait.png differ diff --git a/public/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png b/public/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png new file mode 100644 index 00000000..64a09053 Binary files /dev/null and b/public/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png differ diff --git a/public/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png b/public/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png new file mode 100644 index 00000000..029e83c4 Binary files /dev/null and b/public/splash_screens/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png differ diff --git a/public/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png b/public/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png new file mode 100644 index 00000000..ed0ce416 Binary files /dev/null and b/public/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png differ diff --git a/public/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png b/public/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png new file mode 100644 index 00000000..da53f3e4 Binary files /dev/null and b/public/splash_screens/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png differ diff --git a/public/splash_screens/iPhone_11__iPhone_XR_landscape.png b/public/splash_screens/iPhone_11__iPhone_XR_landscape.png new file mode 100644 index 00000000..386e0920 Binary files /dev/null and b/public/splash_screens/iPhone_11__iPhone_XR_landscape.png differ diff --git a/public/splash_screens/iPhone_11__iPhone_XR_portrait.png b/public/splash_screens/iPhone_11__iPhone_XR_portrait.png new file mode 100644 index 00000000..590a317e Binary files /dev/null and b/public/splash_screens/iPhone_11__iPhone_XR_portrait.png differ diff --git a/public/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png b/public/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png new file mode 100644 index 00000000..7bd624b3 Binary files /dev/null and b/public/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png differ diff --git a/public/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png b/public/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png new file mode 100644 index 00000000..a70fae80 Binary files /dev/null and b/public/splash_screens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png differ diff --git a/public/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png b/public/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png new file mode 100644 index 00000000..1b06793b Binary files /dev/null and b/public/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png differ diff --git a/public/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png b/public/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png new file mode 100644 index 00000000..88a9afdd Binary files /dev/null and b/public/splash_screens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png differ diff --git a/public/splash_screens/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png b/public/splash_screens/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png new file mode 100644 index 00000000..d40426eb Binary files /dev/null and b/public/splash_screens/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png differ diff --git a/public/splash_screens/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png b/public/splash_screens/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png new file mode 100644 index 00000000..0496f4c7 Binary files /dev/null and b/public/splash_screens/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png differ diff --git a/public/splash_screens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_landscape.png b/public/splash_screens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_landscape.png new file mode 100644 index 00000000..582988cb Binary files /dev/null and b/public/splash_screens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_landscape.png differ diff --git a/public/splash_screens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait.png b/public/splash_screens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait.png new file mode 100644 index 00000000..4101bc0e Binary files /dev/null and b/public/splash_screens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait.png differ diff --git a/public/splash_screens/iPhone_15_Pro__iPhone_15__iPhone_14_Pro_landscape.png b/public/splash_screens/iPhone_15_Pro__iPhone_15__iPhone_14_Pro_landscape.png new file mode 100644 index 00000000..a88d6e00 Binary files /dev/null and b/public/splash_screens/iPhone_15_Pro__iPhone_15__iPhone_14_Pro_landscape.png differ diff --git a/public/splash_screens/iPhone_15_Pro__iPhone_15__iPhone_14_Pro_portrait.png b/public/splash_screens/iPhone_15_Pro__iPhone_15__iPhone_14_Pro_portrait.png new file mode 100644 index 00000000..2c56a023 Binary files /dev/null and b/public/splash_screens/iPhone_15_Pro__iPhone_15__iPhone_14_Pro_portrait.png differ diff --git a/public/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png b/public/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png new file mode 100644 index 00000000..e1646b9c Binary files /dev/null and b/public/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png differ diff --git a/public/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png b/public/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png new file mode 100644 index 00000000..2e1f04b5 Binary files /dev/null and b/public/splash_screens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png differ diff --git a/public/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png b/public/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png new file mode 100644 index 00000000..c6bd57a4 Binary files /dev/null and b/public/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png differ diff --git a/public/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png b/public/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png new file mode 100644 index 00000000..24788f1a Binary files /dev/null and b/public/splash_screens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png differ diff --git a/public/splash_screens/icon.png b/public/splash_screens/icon.png new file mode 100644 index 00000000..a43a415d Binary files /dev/null and b/public/splash_screens/icon.png differ diff --git a/src/@types/country-language.d.ts b/src/@types/country-language.d.ts new file mode 100644 index 00000000..c0badd74 --- /dev/null +++ b/src/@types/country-language.d.ts @@ -0,0 +1,21 @@ +declare module "@ladjs/country-language" { + export interface LanguageObj { + countries: Array<{ + code_2: string; + code_3: string; + numCode: string; + }>; + direction: "RTL" | "LTR"; + name: string[]; + nativeName: string[]; + iso639_1: string; + } + + type Callback = (err: null | string, result: null | T) => void; + + declare namespace lib { + function getLanguage(locale: string, cb: Callback): void; + } + + export = lib; +} diff --git a/src/assets/README.md b/src/assets/README.md new file mode 100644 index 00000000..d3820f45 --- /dev/null +++ b/src/assets/README.md @@ -0,0 +1,9 @@ +# About the languages + +Locales are difficult, here is some guidance. + +## Process on adding new languages +1. Use Weblate to add translations, see contributing guidelines. +2. Add your language to `@/assets/languages.ts`. Must be in ISO format (ISO-639 for language and ISO-3166 for country/region). For joke languages, use any format. +3. If the language code doesn't have a region specified (Such as in `pt-BR`, `BR` being the region), add a default region in `@/utils/language.ts` at `defaultLanguageCodes` +4. If the language code doesn't contain a region (Such as in `zh-Hant`), add a default country in `@/utils/language.ts` at `countryPriority`. diff --git a/src/assets/css/index.css b/src/assets/css/index.css new file mode 100644 index 00000000..08b24f68 --- /dev/null +++ b/src/assets/css/index.css @@ -0,0 +1,233 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +html, +body { + @apply bg-background-main font-open-sans text-type-text; + min-height: 100vh; + min-height: 100dvh; +} + +html[data-full], +html[data-full] body { + overscroll-behavior-y: none; +} + +body[data-no-scroll] { + overflow-y: hidden; + height: 100vh; +} + +#root { + padding: 0.05px; + min-height: 100vh; + min-height: 100dvh; + width: 100%; +} + +body[data-no-select] { + user-select: none; +} + +html[data-no-scroll], html[data-no-scroll] body { + overflow: hidden; +} + +.roll { + animation: roll 1s; +} + +.roll-infinite { + animation: roll 2s infinite; +} + +@keyframes roll { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +.line-clamp { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.google-cast-button:not(.casting) google-cast-launcher { + @apply brightness-[500]; +} + +.is-mobile-view .overflow-y-auto { + height: 60vh; +} + +.h-screen { + height: 100vh; + height: 100dvh; +} + +.min-h-screen { + min-height: 100vh; + min-height: 100dvh; +} + +/*generated with Input range slider CSS style generator (version 20211225) +https://toughengineer.github.io/demo/slider-styler*/ +:root { + --slider-height: 0.25rem; + --slider-border-radius: 1em; + --slider-progress-background: #8652bb; +} + +input[type=range].styled-slider { + height: var(--slider-height); + -webkit-appearance: none; + appearance: none; + border-radius: var(--slider-border-radius); + background: #1C161B; +} + +/*progress support*/ +input[type=range].styled-slider.slider-progress { + --range: calc(var(--max) - var(--min)); + --ratio: calc((var(--value) - var(--min)) / var(--range)); + --sx: calc(0.5 * 1rem + var(--ratio) * (100% - 1rem)); +} + +/*webkit*/ +input[type=range].styled-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 1rem; + height: 1rem; + border-radius: var(--slider-border-radius); + background: #FFFFFF; + border: none; + box-shadow: 0 0 2px #000000; + margin-top: calc(0.25em * 0.5 - 1rem * 0.5); +} + +input[type=range].styled-slider::-webkit-slider-runnable-track { + height: var(--slider-height); + border: none; + box-shadow: none; + border-radius: var(--slider-border-radius); +} + +input[type=range].styled-slider::-webkit-slider-thumb:hover { + background: #DCDCDC; +} + +input[type=range].styled-slider.slider-progress::-webkit-slider-runnable-track { + background: linear-gradient(var(--slider-progress-background), var(--slider-progress-background)) 0/var(--sx) 100% no-repeat, #1C161B; +} + +/*mozilla*/ +input[type=range].styled-slider::-moz-range-thumb { + width: 1rem; + height: 1rem; + border-radius: var(--slider-border-radius); + background: #FFFFFF; + border: none; + box-shadow: 0 0 2px #000000; +} + +input[type=range].styled-slider::-moz-range-track { + height: var(--slider-height); + border: none; + border-radius: var(--slider-border-radius); + background: #1C161B; + box-shadow: none; +} + +input[type=range].styled-slider::-moz-range-thumb:hover { + background: #DCDCDC; +} + +input[type=range].styled-slider.slider-progress::-moz-range-track { + background: linear-gradient(var(--slider-progress-background), var(--slider-progress-background)) 0/var(--sx) 100% no-repeat, #1C161B; +} + +/*ms*/ +input[type=range].styled-slider::-ms-fill-upper { + background: transparent; + border-color: transparent; +} + +input[type=range].styled-slider::-ms-fill-lower { + background: transparent; + border-color: transparent; +} + +input[type=range].styled-slider::-ms-thumb { + width: 1rem; + height: 1rem; + border-radius: var(--slider-border-radius); + background: #FFFFFF; + border: none; + box-shadow: 0 0 2px #000000; + margin-top: 0; + box-sizing: border-box; +} + +input[type=range].styled-slider::-ms-track { + height: var(--slider-height); + border-radius: var(--slider-border-radius); + background: #1C161B; + border: none; + box-shadow: none; + box-sizing: border-box; +} + +input[type=range].styled-slider::-ms-thumb:hover { + background: #DCDCDC; +} + +input[type=range].styled-slider.slider-progress::-ms-fill-lower { + height: var(--slider-height); + border-radius: var(--slider-border-radius) 0 0 5px; + margin: -undefined 0 -undefined -undefined; + background: var(--slider-progress-background); + border: none; + border-right-width: 0; +} + +::-webkit-scrollbar-track { + background-color: transparent; +} + +::-webkit-scrollbar-thumb { + background-color: theme("colors.video.context.border"); + border: 5px solid transparent; + border-left: 0; + background-clip: content-box; +} + +::-webkit-scrollbar { + /* For some reason the styles don't get applied without the width */ + width: 13px; +} + +.grecaptcha-badge { + display: none !important; +} + +.tabbable:focus-visible { + 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; +} diff --git a/src/assets/languages.ts b/src/assets/languages.ts new file mode 100644 index 00000000..192f4d6e --- /dev/null +++ b/src/assets/languages.ts @@ -0,0 +1,88 @@ +import ar from "@/assets/locales/ar.json"; +import bg from "@/assets/locales/bg.json"; +import bn from "@/assets/locales/bn.json"; +import ca from "@/assets/locales/ca.json"; +import cs from "@/assets/locales/cs.json"; +import de from "@/assets/locales/de.json"; +import el from "@/assets/locales/el.json"; +import en from "@/assets/locales/en.json"; +import es from "@/assets/locales/es.json"; +import et from "@/assets/locales/et.json"; +import fa from "@/assets/locales/fa.json"; +import fr from "@/assets/locales/fr.json"; +import gl from "@/assets/locales/gl.json"; +import gu from "@/assets/locales/gu.json"; +import he from "@/assets/locales/he.json"; +import hi from "@/assets/locales/hi.json"; +import id from "@/assets/locales/id.json"; +import is from "@/assets/locales/is-IS.json"; +import it from "@/assets/locales/it.json"; +import ja from "@/assets/locales/ja.json"; +import ko from "@/assets/locales/ko.json"; +import lv from "@/assets/locales/lv.json"; +import minion from "@/assets/locales/minion.json"; +import ne from "@/assets/locales/ne.json"; +import nl from "@/assets/locales/nl.json"; +import pa from "@/assets/locales/pa.json"; +import pirate from "@/assets/locales/pirate.json"; +import pl from "@/assets/locales/pl.json"; +import ptbr from "@/assets/locales/pt-BR.json"; +import ptpt from "@/assets/locales/pt-PT.json"; +import ro from "@/assets/locales/ro.json"; +import ru from "@/assets/locales/ru.json"; +import sl from "@/assets/locales/sl.json"; +import sv from "@/assets/locales/sv.json"; +import ta from "@/assets/locales/ta.json"; +import th from "@/assets/locales/th.json"; +import tok from "@/assets/locales/tok.json"; +import tr from "@/assets/locales/tr.json"; +import uk from "@/assets/locales/uk.json"; +import vi from "@/assets/locales/vi.json"; +import zhhant from "@/assets/locales/zh-Hant.json"; +import zh from "@/assets/locales/zh.json"; + +export const locales = { + en, + ca, + cs, + de, + fr, + it, + nl, + pl, + tr, + vi, + zh, + he, + sv, + pirate, + minion, + lv, + th, + ne, + ar, + es, + et, + tok, + hi, + "pt-BR": ptbr, + "pt-PT": ptpt, + uk, + bg, + bn, + el, + fa, + gu, + id, + ja, + ko, + sl, + ta, + "zh-HANT": zhhant, + is, + ru, + gl, + pa, + ro, +}; +export type Locales = keyof typeof locales; diff --git a/src/assets/locales/ar.json b/src/assets/locales/ar.json new file mode 100644 index 00000000..91f0b052 --- /dev/null +++ b/src/assets/locales/ar.json @@ -0,0 +1,416 @@ +{ + "about": { + "description": "sudo-flix هو تطبيق ويب يبحث في الإنترنت عن بثوث. يهدف الفريق إلى تبني نهج معظمه بسيط في استهلاك المحتوى.", + "faqTitle": "الأسئلة الشائعة", + "q1": { + "body": "لا يستضيف sudo-flix أي محتوى. عندما تنقر فوق شيء للمشاهدة، يتم البحث على الإنترنت عن الوسائط المحددة (يمكنك رؤية المصدر الذي تستخدمه على شاشة التحميل وفي علامة تبويب \"مصادر الفيديو\"). لا يتم رفع الوسائط أبدًا عن طريق sudo-flix، كل شيء يتم من خلال آلية البحث هذه.", + "title": "من أين يأتي المحتوى؟" + }, + "q2": { + "body": "لا يمكن طلب عرض أو فيلم، لأن sudo-flix لا يدير أي محتوى. يتم مشاهدة جميع المحتويات من خلال مصادر على الإنترنت.", + "title": "أين يمكنني طلب مسلسل أو فلم؟" + }, + "q3": { + "body": "نتائج البحث لدينا مدعومة بقاعدة بيانات الأفلام (TMDB) وتظهر بغض النظر عما إذا كانت مصادرنا تتضمن فعليًا المحتوى أم لا.", + "title": "تعرِضُ نتائجُ البحثِ الخاصِّ بالعرض أو الفيلم اللذي أريد، لكن لماذا لا يمكنني تشغيله؟" + }, + "title": "حول sudo-flix" + }, + "actions": { + "copied": "تم النسخ", + "copy": "نسخ" + }, + "auth": { + "createAccount": "ليس لديك حساب بعد؟ <0>أنشئ حسابًا.", + "deviceNameLabel": "اسم الجهاز", + "deviceNamePlaceholder": "الهاتف الشخصي", + "generate": { + "description": "جملة مروركَ هي بمثابة اسم مستخدمٍ وكلمة مرورٍ. تأكد من حفظها بشكل آمن، ستحتاجها لتسجيل الدخول إلى حسابك", + "next": "لقد قمتُ بحفظ جملة مروري", + "passphraseFrameLabel": "جملة المرور", + "title": "جملة مرورك" + }, + "hasAccount": "لديك حساب بالفعل؟ <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}} - يرجى تأكيد أنك تثق فيه قبل إنشاء حساب", + "no": "عودة", + "title": "هل تثِقُ في هذا الخادم؟", + "yes": "أثِقُ في هذا الخادم" + }, + "verify": { + "description": "رجاءً أدخل جملة المرور السابقة لتأكيد أنك قد قمت بحفظها ولإنشاء حسابك", + "invalidData": "البيانات غير صالحة", + "noMatch": "جملة المرور غير متطابقة", + "passphraseLabel": "جملة مرورك المكونة من 12 كلمة", + "recaptchaFailed": "فشل التحقق من ReCaptcha", + "register": "إنشاء حساب", + "title": "قم بتأكيد جملة مرورك" + } + }, + "errors": { + "badge": "تم كسره", + "details": "تفاصيل الخطأ", + "reloadPage": "أعد تحميل الصفحة", + "showError": "عرض تفاصيل الخطأ", + "title": "واجهنا خطأ!" + }, + "footer": { + "legal": { + "disclaimer": "تنويه", + "disclaimerText": "لا يستضيف sudo-flix أي ملفات، بل يقوم بالربط مع خدمات طرف ثالث. يجب معالجة المشاكل القانونية مع مضيفي الملفات والمزودين. لا يتحمل sudo-flix مسؤولية أي ملفات يعرضها مزودو الفيديو." + }, + "links": { + "discord": "ديسكورد", + "dmca": "DMCA", + "github": "غيت هاب" + }, + "tagline": "شاهد عروضك وأفلامك المفضلة باستخدام تطبيق البث مفتوح المصدر هذا." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "حَول", + "dmca": "DMCA", + "login": "تسجيل الدخول", + "pagetitle": "\"{{title}} - sudo-flix\"", + "register": "إنشاء حساب", + "settings": "الإعدادات" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "علامات مرجعية" + }, + "continueWatching": { + "sectionTitle": "متابعة المشاهدة" + }, + "mediaList": { + "stopEditing": "إنهاء التعديل" + }, + "search": { + "allResults": "هذا كل ما لدينا!", + "failed": "تعذر العثور على الوسائط، حاول مجددا!", + "loading": "جار التحميل...", + "noResults": "لم نتمكن من العثور على أي شيء!", + "placeholder": "ماذا تريد أن تشاهد؟", + "sectionTitle": "نتائج البحث" + }, + "titles": { + "day": { + "default": "ماذا تريد أن تشاهد في هذه الظهيرة؟", + "extra": ["متشوق للمغامرة؟ قد يكون Jurassic Park خيارًا مثاليًا لك."] + }, + "morning": { + "default": "ماذا تريد أن تشاهد في هذا الصباح؟", + "extra": ["سمعت أن فلم \"Before Sunrise\" جيد"] + }, + "night": { + "default": "ماذا تريد أن تشاهد في هذه الليلة؟", + "extra": ["مُرهَق؟ سمعت أن فيلم \"The Exorcist\" جيد."] + } + } + }, + "media": { + "episodeDisplay": "\"S{{season}} E{{episode}}\"", + "types": { + "movie": "فِلم", + "show": "سِلسلة" + } + }, + "navigation": { + "banner": { + "offline": "تحقق من اتصالك بالأنترنت" + }, + "menu": { + "about": "عنا", + "donation": "تبرع", + "logout": "تسجيل الخروج", + "register": "مزامنة إلى االتخزين لسحابي", + "settings": "الإعدادات", + "support": "الدعم" + } + }, + "notFound": { + "badge": "صفحة غير موجودة", + "goHome": "عودة", + "message": "بحثنا في كل مكان: في الخزانة وحتى تحت الصناديق، ومع ذلك لم نعثر عن الصفحة التي طلبتَها.", + "title": "تعذر العثور على هذه الصفحة" + }, + "overlays": { + "close": "إغلاق" + }, + "player": { + "back": { + "default": "عودة", + "short": "عُدْ" + }, + "casting": { + "enabled": "جارٍ العرض على الجهاز..." + }, + "menus": { + "downloads": { + "disclaimer": "يتم أخذ التنزيلات مباشرةً من قبل مقدم الخدمة. sudo-flix لاسيطرة له على كيفية توفير تلك التنزيلات.", + "downloadPlaylist": "تنزيل قائمة التشغيل", + "downloadSubtitle": "تنزيل مقاطع الترجمة الحالية", + "downloadVideo": "تنزيل الفيديو", + "hlsDisclaimer": "يتم أخذ التنزيلات مباشرة من مزود الخدمة. sudo-flix ليس المتحكم في كيفية توفير التنزيلات. يرجى ملاحظة أنك تقوم بتنزيل قائمة تشغيل HLS، وهذا أمر يتطلب ان تكون على دراية بتقنيات متقدمة لبث الوسائط.", + "onAndroid": { + "1": "للتنزيل على أندرويد، أنقر زر التنزيل وفي الصفحة الموالية إضغط باستمرار على الفيديو، وبعدها اختر حفظ.", + "shortTitle": "تنزيل / Android", + "title": "تنزيل على Android" + }, + "onIos": { + "1": "للتنزيل على نظام iOS، انقر على زر التنزيل ثم، على الصفحة الجديدة، انقر على ، ثم حفظ إلى الملفات .", + "shortTitle": "تنزيل / iOS", + "title": "تنزيل على iOS" + }, + "onPc": { + "1": "على الحاسوب، انقر على زر التنزيل، ثم على الصفحة الجديدة، انقر بزر الفأرة الأيمن على الفيديو وحدد حفظ الفيديو باسم", + "shortTitle": "تنزيل / حاسوب", + "title": "تنزيل على الحاسوب" + }, + "title": "تنزيل" + }, + "episodes": { + "button": "الحلقات", + "emptyState": "لا توجد حلقات في هذا الموسم، يرجى التحقق لاحقًا!", + "episodeBadge": "E{{episode}}", + "loadingError": "خطأ في تحميل الموسم", + "loadingList": "تحميل...", + "loadingTitle": "تحميل...", + "unairedEpisodes": "تم تعطيل حلقة واحدة أو أكثر من هذا الموسم لأنه لم يتم بثها بعد." + }, + "playback": { + "speedLabel": "سرعة التشغيل", + "title": "إعدادات التشغيل" + }, + "quality": { + "automaticLabel": "جودة تلقائية", + "hint": "يمكنك محاولة <0>تغيير المصدر للحصول على خيارات جودة مختلفة.", + "iosNoQuality": "نظرًا للقيود المحددة من قبل Apple، خيارات الجودة غير متوفرة على iOS لهذا المصدر. يمكنك محاولة <0>التبديل إلى مصدر آخر للحصول على خيارات جودة مختلفة.", + "title": "جودة" + }, + "settings": { + "downloadItem": "تنزيل", + "enableSubtitles": "تفعيل مقاطع الترجمة", + "experienceSection": "تجربة المشاهدة", + "playbackItem": "إعدادات التشغيل", + "qualityItem": "جودة", + "sourceItem": "مصادر الفيديو", + "subtitleItem": "إعدادات الترجمة", + "videoSection": "إعدادات الفيديو" + }, + "sources": { + "failed": { + "text": "حدثت خطأ أثناء محاولة إيجاد عن أي فيديو، يرجى تجربة مصدر آخر.", + "title": "فشلت عملية الاستخراج" + }, + "noEmbeds": { + "text": "تعذر العثور على أي تضمينات، يرجى تجربة مصدر آخر.", + "title": "لم يتم العثور على تضمينات" + }, + "noStream": { + "text": "هذا المصدر ليس فيه بث لهذا الفلم أو المسلسل.", + "title": "لا بث" + }, + "title": "مصادر", + "unknownOption": "مجهول" + }, + "subtitles": { + "customChoice": "استيراد ترجمة من ملف", + "customizeLabel": "تخصيص", + "offChoice": "تعطيل", + "settings": { + "backlink": "مقاطع ترجمة مخصصة", + "delay": "تأخير مقاطع الترجمة", + "fixCapitals": "تصحيح تهجئة الحروف" + }, + "title": "مقاطع الترجمة", + "unknownLanguage": "مجهول" + } + }, + "metadata": { + "failed": { + "badge": "فشل", + "homeButton": "عُدْ للصفحة الرئيسية", + "text": "تعذر تحميل البيانات الوصفية للوسائط من قاعدة البيانات TMDB. يرجى التحقق مما إذا كانت TMDB غير متاحة أو محظورة على اتصال الإنترنت الخاص بك.", + "title": "فشل في تحميل البيانات الوصفية" + }, + "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": "مرحبًا بكم في صفحة sudo-flix الخاصة بالتواصل حول قانون الألفية للملكية الرقمية (DMCA) ! نحن نحترم حقوق الملكية الفكرية ونرغب في التعامل بسرعة مع أي مسألة تخص حقوق الطبع والنشر. إن كنت تعتقد أن أيا من أعمالك المحميو بحقوق الطبع والنشر قد تم استخدامها بشكل غير لائق على منصتنا، رجاءً أرسل إشعارا مفصلا إلى البريد الإلكتروني أدناه. يرجى تضمين وصف للمواد المحمية بحقوق الطبع والنشر، وكذا طريقةً للتواصل معك، إضافة إلى تصريح بمصداقية طلبك. نحن ملتزمون بحل هذه القضايا بسرعة ونقدر تعاونكم في الحفاظ على sudo-flix كمكان يحترم الإبداع وحقوق الطبع والنشر.", + "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": "المظهر" + }, + "connections": { + "server": { + "description": "إذا كنت ترغب في الاتصال بخادم خلفي مخصص لتخزين بياناتك، قم بتفعيل هذا ووفر الرابط.", + "label": "خادم مُخصص", + "urlLabel": "رابط الخادم المخصص" + }, + "title": "الاتصالات", + "workers": { + "addButton": "إضافة وكيل جديد", + "description": "لتشغيل التطبيق، يتم توجيه كل الاتصالات عبر وكلاء البروكسي. قم بتمكين هذا إذا كنت ترغب في استخدام خوادم العمل الخاصة بك.", + "emptyState": "لا يوجد وكلاء حتى الآن، أضف واحدًا أدناه", + "label": "استخدام وكلاء مُخصصين", + "urlLabel": "روابط الوكلاء", + "urlPlaceholder": "https://" + } + }, + "reset": "إعادة تعيين", + "save": "حفظ", + "sidebar": { + "info": { + "appVersion": "إصدار التطبيق", + "backendUrl": "رابط النهاية الخلفية", + "backendVersion": "إصدار النهاية الخلفية", + "hostname": "اسم المضيف", + "insecure": "غير آمن", + "notLoggedIn": "أنت لم تسجل دخولك بعد", + "secure": "آمن", + "title": "معلومات التطبيق", + "unknownVersion": "غير معروف", + "userId": "معرِّف المستخدم" + } + }, + "subtitles": { + "backgroundLabel": "تعتيم الخلفية", + "colorLabel": "لون", + "previewQuote": "يجب أن لا أخأف. الخوف هو قاتل العقل.", + "textSizeLabel": "حجم النص", + "title": "مقاطع الترجمة" + }, + "unsaved": "لديك تغييرات غير محفوظة" + } +} diff --git a/src/assets/locales/bg.json b/src/assets/locales/bg.json new file mode 100644 index 00000000..643f1484 --- /dev/null +++ b/src/assets/locales/bg.json @@ -0,0 +1,285 @@ +{ + "about": { + "description": "sudo-flix е уеб приложение, което търси в интернет за стриймове. Екипът се стреми към предимно минималистичен подход при консумирането на съдържание.", + "faqTitle": "Общи въпроси", + "q1": { + "body": "sudo-flix не хоства никакво съдържание. Когато кликнете върху нещо, за да го гледате, се търси в интернет избраното медийно съдържание (На екрана за зареждане и в раздела \"Източници на видео\" можете да видите кой източник използвате). Медийното съдържание никога не се качва от sudo-flix, всичко става чрез този механизъм за търсене.", + "title": "От къде идва съдържанието?" + }, + "q2": { + "body": "Невъзможно е да поискате сериал или филм, sudo-flix не управлява никакво съдържание. Всичко се гледа чрез източници в интернет.", + "title": "Къде мога да поискам определен сериал или филм?" + }, + "q3": { + "body": "Резултатите от търсенето ни се поддържат от базата данни за филми (The Movie Database - TMDB) и се показват, независимо дали източниците ни реално разполагат със съдържанието.", + "title": "Резултатите от търсенето показват сериала или филма, защо не мога да го пусна?" + }, + "title": "Относно sudo-flix" + }, + "actions": { + "copied": "Копиране", + "copy": "Копирай" + }, + "auth": { + "createAccount": "Все още нямате акаунт? <0>Създайте акаунт.", + "deviceNameLabel": "Име на устройството", + "deviceNamePlaceholder": "Личен телефон", + "generate": { + "description": "Вашата паролна фраза служи като потребителско име и парола. Уверете се, че я пазите в безопасност, тъй като ще трябва да я въведете, за да влезете в своя акаунт.", + "next": "Запазих паролната си фраза.", + "passphraseFrameLabel": "Паролна фраза", + "title": "Вашата паролна фраза" + }, + "hasAccount": "Вече имате акаунт? <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}} - моля, потвърдете, че му вярвате, преди да създадете акаунт.", + "no": "Върнете се назад", + "title": "Вярвате ли на този сървър?", + "yes": "Вярвам на този сървър." + }, + "verify": { + "description": "Моля, въведете паролната фраза от по-рано, за да потвърдите, че сте я запазили, и за да създадете своя акаунт.", + "invalidData": "Данните не са валидни.", + "noMatch": "Паролната фраза не съвпада.", + "passphraseLabel": "Вашата 12-думена паролна фраза", + "recaptchaFailed": "Валидацията на ReCaptcha не беше успешна.", + "register": "Създаване на акаунт", + "title": "Потвърдете вашата паролна фраза." + } + }, + "errors": { + "badge": "Счупи се", + "details": "Детайли за грешката", + "reloadPage": "Презаредете страницата.", + "showError": "Покажи детайлите за грешката", + "title": "Сблъскахме се с грешка!" + }, + "footer": { + "legal": { + "disclaimer": "Внимание", + "disclaimerText": "sudo-flix не хоства никакви файлове, а просто предоставя връзки към услуги от трета страна. Правните въпроси следва да се разглеждат с доставчиците на файлове. sudo-flix не носи отговорност за никакви медийни файлове, предоставени от видео доставчици." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "Github" + }, + "tagline": "Гледайте любимите си предавания и филми с това отворено-код стрийминг приложение." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "За нас", + "dmca": "DMCA", + "login": "Вход", + "pagetitle": "{{title}} - sudo-flix", + "register": "Регистрация", + "settings": "Настройки" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Отметки" + }, + "continueWatching": { + "sectionTitle": "Продължи гледането" + }, + "mediaList": { + "stopEditing": "Спри редактирането" + }, + "search": { + "allResults": "Това е всичко, което имаме!", + "failed": "Неуспешно намиране на медия, опитайте отново!", + "loading": "Зареждане...", + "noResults": "Не успяхме да намерим нищо!", + "placeholder": "Какво искате да гледате?", + "sectionTitle": "Резултати от търсенето" + }, + "titles": { + "day": { + "default": "Какво бихте искали да гледате този следобед?", + "extra": [ + "Чувствате ли се авантюристично? \"Джурасик Парк\" може да бъде перфектният избор." + ] + }, + "morning": { + "default": "Какво бихте искали да гледате тази сутрин?", + "extra": ["Чух, че Before Sunrise е добър"] + }, + "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": "Търсихме навсякъде: под кошчетата, в килера, зад проксито, но в крайна сметка не успяхме да намерим страницата, която търсите.", + "title": "Тази страница не можа да бъде намерена" + }, + "overlays": { + "close": "Затвори" + }, + "player": { + "back": { + "default": "Обратно към началото", + "short": "Назад" + }, + "casting": { + "enabled": "Предава се към устройството..." + }, + "menus": { + "downloads": { + "disclaimer": "Изтеглянията се вземат директно от доставчика. sudo-flix няма контрол върху това как се предоставят изтеглянията.", + "downloadPlaylist": "Изтегляне на плейлист", + "downloadSubtitle": "Изтеглете текущите субтитри", + "downloadVideo": "Изтегляне на видео", + "hlsDisclaimer": "Изтеглянията се вземат директно от доставчика. sudo-flix няма контрол върху това как се предоставят изтеглянията. Моля, имайте предвид, че изтегляте HLS плейлист, това е предназначено за потребители, запознати с усъвършенствано мултимедийно поточно предаване.", + "onAndroid": { + "1": "За да изтеглите на Android, щракнете върху бутона за изтегляне, след което на новата страница докоснете и задръжте видеоклипа, след което изберете запазване.", + "shortTitle": "Изтегляне / Android", + "title": "Изтегляне на Android" + }, + "onIos": { + "1": "За да изтеглите на iOS, щракнете върху бутона за изтегляне, след което на новата страница щракнете върху , след което върху Запазване във файлове .", + "shortTitle": "Изтегляне / iOS", + "title": "Изтегляне на iOS" + }, + "onPc": { + "1": "На компютър щракнете върху бутона за изтегляне, след което на новата страница щракнете с десния бутон върху видеоклипа и изберете Запазване на видеоклипа като", + "shortTitle": "Изтегляне / компютър", + "title": "Изтегляне на компютър" + }, + "title": "Изтегли" + }, + "episodes": { + "button": "епизоди", + "emptyState": "Няма епизоди в този сезон, проверете отново по-късно!", + "episodeBadge": "E{{episode}}", + "loadingError": "Грешка при зареждането на сезона", + "loadingList": "Зареждане...", + "loadingTitle": "Зареждане..." + }, + "playback": { + "speedLabel": "Скорост на възпроизвеждане", + "title": "Настройки за възпроизвеждане" + }, + "quality": { + "automaticLabel": "Автоматично качество", + "hint": "Можете да опитате <0>да превключите източника, за да получите различни опции за качество.", + "iosNoQuality": "Поради ограниченията, определени от Apple, изборът на качество не е наличен в iOS за този източник. Можете да опитате <0>да превключите към друг източник, за да получите различни опции за качество.", + "title": "качество" + }, + "settings": { + "downloadItem": "Изтегли", + "enableSubtitles": "включи субтитрите", + "experienceSection": "Изживяване при гледане", + "playbackItem": "Настройки за възпроизвеждане", + "qualityItem": "качество", + "sourceItem": "Видео източници", + "subtitleItem": "Настройки на субтитрите", + "videoSection": "Видео настройки" + }, + "sources": { + "failed": { + "text": "Възникна грешка при опит за намиране на видеоклипове, моля, опитайте с друг източник.", + "title": "Неуспешно изстъргване" + }, + "noEmbeds": { + "text": "Не можахме да намерим никакви вграждания, моля, опитайте с друг източник.", + "title": "Няма намерени вграждания" + }, + "noStream": { + "text": "Този източник няма потоци за този филм или шоу.", + "title": "Няма поток" + }, + "title": "Източници", + "unknownOption": "неизвестен" + }, + "subtitles": { + "customChoice": "Качете субтитри", + "customizeLabel": "Персонализиране", + "offChoice": "изключи", + "settings": { + "backlink": "Персонализирани субтитри", + "delay": "Забавяне на субтитрите", + "fixCapitals": "Коригиране на главни букви" + }, + "title": "субтитри", + "unknownLanguage": "неизвестен" + } + }, + "metadata": { + "failed": { + "badge": "Провал", + "homeButton": "Обратно към началото", + "text": "Неуспешно зареждане на метаданните на носителя от TMDB. Моля, проверете дали TMDB не работи или е блокиран във вашата интернет връзка.", + "title": "Неуспешно зареждане на метаданните" + }, + "notFound": { + "badge": "Не е намерено", + "homeButton": "Обратно към началото", + "text": "Не успяхме да намерим медията, която поискахте. Или е бил премахнат, или сте променили URL адреса.", + "title": "Тази медия не можа да бъде намерена." + } + }, + "nextEpisode": { + "cancel": "Отказ", + "next": "Следващ епизод" + }, + "playbackError": { + "badge": "Грешка при възпроизвеждане", + "errors": { + "errorAborted": "Извличането на медията беше прекратено по искане на потребителя.", + "errorDecode": "Въпреки че преди това беше определено като използваем, възникна грешка при опит за декодиране на медийния ресурс, което доведе до грешка.", + "errorGenericMedia": "Възникна неизвестна медийна грешка.", + "errorNetwork": "Възникна някакъв вид мрежова грешка, която попречи на медията да бъде успешно извлечена, въпреки че преди това е била достъпна.", + "errorNotSupported": "Обектът медия или доставчик на медия не се поддържа." + } + } + } +} diff --git a/src/assets/locales/bn.json b/src/assets/locales/bn.json new file mode 100644 index 00000000..987da91f --- /dev/null +++ b/src/assets/locales/bn.json @@ -0,0 +1,279 @@ +{ + "about": { + "description": "মুভি-ওয়েব একটি ওয়েব অ্যাপ্লিকেশন যা স্ট্রিমগুলির জন্য ইন্টারনেট অনুসন্ধান করে। দলটির লক্ষ্য বিষয়বস্তু গ্রহণের জন্য সবচেয়ে সংক্ষিপ্ত পদ্ধতির জন্য।", + "faqTitle": "সাধারণ প্রশ্নাবলী", + "q1": { + "body": "মুভি-ওয়েব কোনো বিষয়বস্তু হোস্ট করে না। আপনি যখন দেখার জন্য কিছুতে ক্লিক করেন, তখন নির্বাচিত মিডিয়ার জন্য ইন্টারনেট অনুসন্ধান করা হয় (লোডিং স্ক্রিনে এবং 'ভিডিও উত্স' ট্যাবে আপনি কোন উত্সটি ব্যবহার করছেন তা দেখতে পারেন)। মিডিয়া কখনই মুভি-ওয়েব দ্বারা আপলোড হয় না, সবকিছু এই অনুসন্ধান প্রক্রিয়ার মাধ্যমে হয়।", + "title": "কন্টেন্ট কোথা থেকে আসে?" + }, + "q2": { + "body": "একটি শো বা সিনেমার অনুরোধ করা সম্ভব নয়, মুভি-ওয়েব কোনো বিষয়বস্তু পরিচালনা করে না। সমস্ত বিষয়বস্তু ইন্টারনেটে উৎসের মাধ্যমে দেখা হয়।", + "title": "আমি কোথায় একটি শো বা সিনেমা অনুরোধ করতে পারি?" + }, + "q3": { + "body": "আমাদের অনুসন্ধানের ফলাফলগুলি The Movie Database (TMDB) দ্বারা চালিত হয় এবং আমাদের উত্সগুলিতে প্রকৃতপক্ষে সামগ্রী আছে কিনা তা নির্বিশেষে প্রদর্শন করা হয়।", + "title": "সার্চ রেজাল্টে শো বা মুভি দেখায়, কেন আমি এটা চালাতে পারি না?" + }, + "title": "মুভি-ওয়েব সম্পর্কে" + }, + "actions": { + "copied": "অনুলিপি করা", + "copy": "অনুলিপি" + }, + "auth": { + "createAccount": "কোনো একাউন্ট এখনও আছে না? <0>একটি অ্যাকাউন্ট তৈরি করুন৷৷", + "deviceNameLabel": "ডিভাইসের নাম", + "deviceNamePlaceholder": "ব্যক্তিগত ফোন", + "generate": { + "description": "আপনার পাসফ্রেজ আপনার ব্যবহারকারীর নাম এবং পাসওয়ার্ড হিসাবে কাজ করে। আপনার অ্যাকাউন্টে লগইন করার জন্য আপনাকে এটি প্রবেশ করতে হবে বলে এটিকে সুরক্ষিত রাখা নিশ্চিত করুন৷", + "next": "আমি আমার পাসফ্রেজ সংরক্ষণ করেছি", + "passphraseFrameLabel": "পাসফ্রেজ", + "title": "আপনার পাসফ্রেজ" + }, + "hasAccount": "ইতিমধ্যে একটি সদস্যপদ আছে? <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}}-এর সাথে সংযোগ করছেন - একটি অ্যাকাউন্ট করার আগে দয়া করে নিশ্চিত করুন যে আপনি এটি বিশ্বাস করেন", + "no": "ফিরে যাও", + "title": "আপনি এই সার্ভার বিশ্বাস করেন?", + "yes": "আমি এই সার্ভার বিশ্বাস" + }, + "verify": { + "description": "আপনি এটি সংরক্ষণ করেছেন তা নিশ্চিত করতে এবং আপনার অ্যাকাউন্ট তৈরি করতে অনুগ্রহ করে আগে থেকে আপনার পাসফ্রেজ লিখুন", + "invalidData": "ডেটা বৈধ নয়", + "noMatch": "পাসফ্রেজ মেলে না", + "passphraseLabel": "আপনার 12-শব্দের পাসফ্রেজ", + "recaptchaFailed": "রিক্যাপচা যাচাইকরণ ব্যর্থ হয়েছে", + "register": "হিসাব তৈরি কর", + "title": "আপনার পাসফ্রেজ নিশ্চিত করুন" + } + }, + "errors": { + "badge": "এটা ভাঙ্গা", + "details": "ভুল তথ্য", + "reloadPage": "পৃষ্ঠাটি পুনরায় লোড করুন", + "showError": "ত্রুটি বিবরণ দেখান", + "title": "আমরা একটি ত্রুটির সম্মুখীন হয়েছি!" + }, + "footer": { + "legal": { + "disclaimer": "দাবিত্যাগ", + "disclaimerText": "মুভি-ওয়েব কোনো ফাইল হোস্ট করে না, এটি শুধুমাত্র তৃতীয় পক্ষের পরিষেবাগুলির সাথে লিঙ্ক করে। ফাইল হোস্ট এবং প্রদানকারীদের সাথে আইনি সমস্যাগুলি নেওয়া উচিত। মুভি-ওয়েব ভিডিও প্রদানকারীদের দ্বারা দেখানো কোনো মিডিয়া ফাইলের জন্য দায়ী নয়।" + }, + "links": { + "discord": "বিরোধ", + "dmca": "ডিএমসিএ", + "github": "গিটহাব" + }, + "tagline": "এই ওপেন সোর্স স্ট্রিমিং অ্যাপের মাধ্যমে আপনার প্রিয় শো এবং সিনেমা দেখুন।" + }, + "global": { + "name": "মুভি-ওয়েব", + "pages": { + "about": "সম্পর্কিত", + "dmca": "ডিএমসিএ", + "login": "প্রবেশ করুন", + "pagetitle": "{{title}} - মুভি-ওয়েব", + "register": "নিবন্ধন", + "settings": "সেটিংস" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "বুকমার্ক" + }, + "continueWatching": { + "sectionTitle": "দেখা চালিয়ে যান" + }, + "mediaList": { + "stopEditing": "সম্পাদনা বন্ধ করুন" + }, + "search": { + "allResults": "যে আমরা সব আছে!", + "failed": "মিডিয়া খুঁজে পেতে ব্যর্থ, আবার চেষ্টা করুন!", + "loading": "লোড হচ্ছে..।", + "noResults": "আমরা কিছুই খুঁজে পাইনি!", + "placeholder": "আপনি কি দেখতে চান?", + "sectionTitle": "অনুসন্ধান ফলাফল" + }, + "titles": { + "day": { + "default": "আপনি এই বিকেলে কি দেখতে চান?", + "extra": ["দুঃসাহসিক বোধ করছেন? জুরাসিক পার্ক নিখুঁত পছন্দ হতে পারে।"] + }, + "morning": { + "default": "আপনি এই সকালে কি দেখতে চান?", + "extra": ["শুনি সূর্যোদয়ের আগে ভালো"] + }, + "night": { + "default": "আপনি আজ রাতে কি দেখতে চান?", + "extra": ["ক্লান্ত? আমি শুনেছি দ্য এক্সরসিস্ট ভাল।"] + } + } + }, + "media": { + "episodeDisplay": "সিজন{{সিজন}} পর্ব{{পর্ব}}", + "types": { + "movie": "সিনেমা", + "show": "দেখান" + } + }, + "navigation": { + "banner": { + "offline": "আপনার ইন্টারনেট সংযোগ পরীক্ষা করুন" + }, + "menu": { + "about": "আমাদের সম্পর্কে", + "donation": "দান করুন", + "logout": "প্রস্থান", + "register": "ক্লাউডে সিঙ্ক করুন", + "settings": "সেটিংস", + "support": "সমর্থন" + } + }, + "notFound": { + "badge": "পাওয়া যায়নি", + "goHome": "বাড়িতে ফিরে যাও", + "message": "আমরা সর্বত্র তাকালাম: বিনের নীচে, পায়খানায়, প্রক্সির পিছনে কিন্তু শেষ পর্যন্ত আপনি যে পৃষ্ঠাটি খুঁজছেন তা খুঁজে পাইনি।", + "title": "সেই পৃষ্ঠাটি খুঁজে পাওয়া যায়নি" + }, + "overlays": { + "close": "বন্ধ" + }, + "player": { + "back": { + "default": "বাড়িতে ফিরে যাও", + "short": "পেছনে" + }, + "casting": { + "enabled": "ডিভাইসে কাস্ট করা হচ্ছে..।" + }, + "menus": { + "downloads": { + "disclaimer": "ডাউনলোড সরাসরি প্রদানকারী থেকে নেওয়া হয়. কিভাবে ডাউনলোড দেওয়া হয় তার উপর মুভি-ওয়েবের নিয়ন্ত্রণ নেই।", + "downloadPlaylist": "প্লেলিস্ট ডাউনলোড করুন", + "downloadSubtitle": "বর্তমান সাবটাইটেল ডাউনলোড করুন", + "downloadVideo": "ভিডিও ডাউনলোড", + "hlsDisclaimer": "ডাউনলোড সরাসরি প্রদানকারী থেকে নেওয়া হয়. কিভাবে ডাউনলোড দেওয়া হয় তার উপর মুভি-ওয়েবের নিয়ন্ত্রণ নেই। অনুগ্রহ করে মনে রাখবেন যে আপনি একটি HLS প্লেলিস্ট ডাউনলোড করছেন, এটি উন্নত মাল্টিমিডিয়া স্ট্রিমিংয়ের সাথে পরিচিত ব্যবহারকারীদের জন্য।", + "onAndroid": { + "1": "অ্যান্ড্রয়েডে ডাউনলোড করতে, ডাউনলোড বোতামে ক্লিক করুন তারপর, নতুন পৃষ্ঠায়, ভিডিওতে ট্যাপ করুন এবং ধরে রাখুন, তারপরে সংরক্ষণ করুন নির্বাচন করুন।", + "shortTitle": "ডাউনলোড/অ্যান্ড্রয়েড", + "title": "অ্যান্ড্রয়েডে ডাউনলোড হচ্ছে" + }, + "onIos": { + "1": "iOS-এ ডাউনলোড করতে, ডাউনলোড বোতামে ক্লিক করুন তারপর, নতুন পৃষ্ঠায়, -এ ক্লিক করুন, তারপর ফাইলে সংরক্ষণ করুন ।", + "shortTitle": "ডাউনলোড / iOS", + "title": "iOS এ ডাউনলোড হচ্ছে" + }, + "onPc": { + "1": "পিসিতে, ডাউনলোড বোতামে ক্লিক করুন তারপর, নতুন পৃষ্ঠায়, ভিডিওটিতে ডান ক্লিক করুন এবং ভিডিওটিকে এই হিসাবে সংরক্ষণ করুন নির্বাচন করুন", + "shortTitle": "ডাউনলোড/পিসি", + "title": "পিসিতে ডাউনলোড হচ্ছে" + }, + "title": "ডাউনলোড করুন" + }, + "episodes": { + "button": "পর্বগুলি", + "emptyState": "এই মরসুমে কোন পর্ব নেই, পরে আবার চেক করুন!", + "episodeBadge": "E{{episode}}", + "loadingError": "ঋতু লোড করার সময় ত্রুটি৷", + "loadingList": "লোড হচ্ছে..।", + "loadingTitle": "লোড হচ্ছে..।", + "unairedEpisodes": "এই সিজনের এক বা একাধিক পর্ব অক্ষম করা হয়েছে কারণ সেগুলি এখনও সম্প্রচার করা হয়নি।" + }, + "playback": { + "speedLabel": "প্লেব্যাক গতি", + "title": "প্লেব্যাক সেটিংস" + }, + "quality": { + "automaticLabel": "স্বয়ংক্রিয় গুণমান", + "hint": "আপনি বিভিন্ন গুণমানের বিকল্প পেতে <0>উৎস পরিবর্তন করে দেখতে পারেন।", + "iosNoQuality": "অ্যাপল-সংজ্ঞায়িত সীমাবদ্ধতার কারণে, এই উৎসের জন্য গুণমান নির্বাচন iOS-এ উপলব্ধ নয়। বিভিন্ন মানের বিকল্প পেতে আপনি <0>অন্য উৎসে স্যুইচ করার চেষ্টা করতে পারেন।", + "title": "গুণমান" + }, + "settings": { + "downloadItem": "ডাউনলোড করুন", + "enableSubtitles": "সাবটাইটেল সক্রিয় করুন", + "experienceSection": "দেখার অভিজ্ঞতা", + "playbackItem": "প্লেব্যাক সেটিংস", + "qualityItem": "গুণমান", + "sourceItem": "ভিডিও সূত্র", + "subtitleItem": "সাবটাইটেল সক্রিয় করুন", + "videoSection": "ভিডিও সেটিংস" + }, + "sources": { + "failed": { + "text": "কোনো ভিডিও খুঁজে বের করার চেষ্টা করার সময় একটি ত্রুটি ছিল, অনুগ্রহ করে একটি ভিন্ন উত্স চেষ্টা করুন।", + "title": "স্ক্র্যাপ করতে ব্যর্থ" + }, + "noEmbeds": { + "text": "আমরা কোনো এম্বেড খুঁজে পেতে অক্ষম, একটি ভিন্ন উৎস চেষ্টা করুন।", + "title": "কোনো এম্বেড পাওয়া যায়নি" + }, + "noStream": { + "text": "এই উৎসের এই সিনেমা বা শোর জন্য কোনো স্ট্রিম নেই।", + "title": "অজানা" + }, + "title": "সূত্র", + "unknownOption": "অজানা" + }, + "subtitles": { + "customChoice": "ফাইল থেকে সাবটাইটেল নির্বাচন করুন", + "customizeLabel": "কাস্টমাইজ করুন", + "offChoice": "বন্ধ", + "settings": { + "backlink": "কাস্টম সাবটাইটেল", + "delay": "সাবটাইটেল বিলম্ব", + "fixCapitals": "মূলধন ঠিক করুন" + }, + "title": "সাবটাইটেল", + "unknownLanguage": "অজানা" + } + }, + "metadata": { + "api": { + "text": "API মেটাডেটা লোড করা যায়নি, অনুগ্রহ করে আপনার ইন্টারনেট সংযোগ পরীক্ষা করুন।", + "title": "API মেটাডেটা লোড করতে ব্যর্থ হয়েছে" + }, + "failed": { + "badge": "ব্যর্থ", + "homeButton": "বাড়িতে যেতে", + "text": "TMDB থেকে মিডিয়ার মেটাডেটা লোড করা যায়নি। আপনার ইন্টারনেট সংযোগে TMDB ডাউন বা ব্লক করা আছে কিনা তা অনুগ্রহ করে চেক করুন।", + "title": "মেটাডেটা লোড করতে ব্যর্থ হয়েছে" + }, + "notFound": { + "badge": "পাওয়া যায়নি", + "homeButton": "বাড়িতে ফিরে যাও", + "text": "আপনার অনুরোধ করা মিডিয়া আমরা খুঁজে পাইনি। হয় এটি সরানো হয়েছে অথবা আপনি URL-এর সাথে হেরফের করেছেন।" + } + }, + "turnstile": { + "description": "ডানদিকে ক্যাপচা সম্পূর্ণ করে আপনি যে মানুষ তা যাচাই করুন। সিনেমা-ওয়েবকে নিরাপদ রাখতেই এই!", + "error": "আপনার মানবতা যাচাই করতে ব্যর্থ হয়েছে. অনুগ্রহপূর্বক আবার চেষ্টা করুন।", + "title": "আপনি যে মানুষ তা আমাদের যাচাই করতে হবে।", + "verifyingHumanity": "আপনার মানবতা যাচাই করা হচ্ছে..।" + } + } +} diff --git a/src/assets/locales/ca.json b/src/assets/locales/ca.json new file mode 100644 index 00000000..729bf083 --- /dev/null +++ b/src/assets/locales/ca.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "sudo-flix és una aplicació web que cerca fluxos a internet. L'equip té com a objectiu un enfocament majoritàriament minimalista del consum de contingut.", + "faqTitle": "Preguntes freqüents", + "q1": { + "body": "sudo-flix no allotja cap contingut. Quan feu clic a alguna cosa per a mirar-la, es busca a Internet el contingut seleccionat (a la pantalla de càrrega i a la pestanya «Fonts de vídeo» podeu veure quina font utilitzeu). sudo-flix mai hi puja contingut, tot és a través d'aquest mecanisme de cerca.", + "title": "D'on prové el contingut?" + }, + "q2": { + "body": "No és possible sol·licitar un programa o una pel·lícula, sudo-flix no gestiona cap contingut. Tot el contingut es visualitza a través de fonts a internet.", + "title": "On puc sol·licitar un programa o una pel·lícula?" + }, + "q3": { + "body": "Els resultats de cerca funcionen amb The Movie Database (TMDB) i es mostren independentment de si les nostres fonts realment tenen el contingut.", + "title": "Els resultats de la cerca mostren el programa o la pel·lícula, per què no puc reproduir-lo?" + }, + "title": "Sobre sudo-flix" + }, + "actions": { + "copied": "S'ha copiat", + "copy": "Copia" + }, + "auth": { + "createAccount": "Encara no teniu un compte? <0>Creeu un compte.", + "deviceNameLabel": "Nom del dispositiu", + "deviceNamePlaceholder": "Telèfon personal", + "generate": { + "description": "La frase de contrasenya actua com a nom d'usuari i contrasenya. Assegureu-vos de mantenir-la segura, ja que haureu d'introduir-la per a iniciar la sessió al vostre compte", + "next": "He desat la frase de contrasenya", + "passphraseFrameLabel": "Frase de contrasenya", + "title": "La vostra frase de contrasenya" + }, + "hasAccount": "Ja teniu un compte? <0>Inicieu sessió aquí.", + "login": { + "description": "Introduïu la vostra frase de contrasenya per a iniciar sessió al vostre compte", + "deviceLengthError": "Introduïu un nom per al dispositiu", + "passphraseLabel": "Frase de contrasenya de 12 paraules", + "passphrasePlaceholder": "Frase de contrasenya", + "submit": "Inicia sessió", + "title": "Inicieu sessió al vostre compte", + "validationError": "Frase de contrasenya incorrecta o incompleta" + }, + "register": { + "information": { + "color1": "Color de perfil 1", + "color2": "Color de perfil 2", + "header": "Introduïu un nom per al dispositiu i trieu els colors i la icona d'usuari que vulgueu", + "icon": "Icona d'usuari", + "next": "Següent", + "title": "Informació del compte" + } + }, + "trust": { + "failed": { + "text": "L'heu configurada correctament?", + "title": "No s'ha pogut accedir al servidor" + }, + "host": "Us esteu connectant a <0>{{hostname}}. Confirmeu que hi confieu abans de crear un compte", + "no": "Torna", + "title": "Confieu en aquest servidor?", + "yes": "Confie en aquest servidor" + }, + "verify": { + "description": "Introduïu la vostra frase de contrasenya anterior per a confirmar que l'heu desat, i crear el compte", + "invalidData": "La data no és vàlida", + "noMatch": "La frase de contrasenya no coincideix", + "passphraseLabel": "Frase de contrasenya de 12 paraules", + "recaptchaFailed": "Ha fallat la validació de ReCaptcha", + "register": "Crea el compte", + "title": "Confirmeu la frase de contrasenya" + } + }, + "errors": { + "badge": "S'ha trencat", + "details": "Detalls de l'error", + "reloadPage": "Recarrega la pàgina", + "showError": "Mostra els detalls de l'error", + "title": "Hem trobat un error!" + }, + "footer": { + "legal": { + "disclaimer": "Avís d'exempció de responsabilitat", + "disclaimerText": "sudo-flix no allotja cap fitxer, només enllaça a serveis de tercers. Els problemes legals s'han d'abordar amb qui allotja i els proveïdors de fitxers. sudo-flix no es fa responsable del contingut mostrat pels proveïdors de vídeo." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Mireu els vostres programes i pel·lícules preferits amb aquesta aplicació de codi obert de reproducció en temps real." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Quant a", + "dmca": "DMCA", + "login": "Inicia sessió", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registra", + "settings": "Configuració" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Marcadors" + }, + "continueWatching": { + "sectionTitle": "Continueu mirant" + }, + "mediaList": { + "stopEditing": "Deixa d'editar" + }, + "search": { + "allResults": "Això és tot el que tenim!", + "failed": "No s'ha pogut trobar cap contingut, torneu-ho a provar!", + "loading": "S'està carregant…", + "noResults": "No hem pogut trobar res!", + "placeholder": "Què voleu mirar?", + "sectionTitle": "Resultats de la cerca" + }, + "titles": { + "day": { + "default": "Què us agradaria mirar aquesta vesprada/tarda?", + "extra": [ + "Voleu aventura? Jurassic Park podria ser l'elecció perfecta." + ] + }, + "morning": { + "default": "Què us agradaria mirar aquest matí?", + "extra": ["He sentit que «Abans de l'alba» és bona"] + }, + "night": { + "default": "Què us agradaria mirar aquesta nit?", + "extra": ["Esteu cansat? He sentit que «L'exorcista» és bona."] + } + } + }, + "media": { + "episodeDisplay": "T{{season}} E{{episode}}", + "types": { + "movie": "Pel·lícula", + "show": "Sèrie/Programa" + } + }, + "navigation": { + "banner": { + "offline": "Comproveu la connexió a internet" + }, + "menu": { + "about": "Quant a nosaltres", + "donation": "Feu una donació", + "logout": "Tanca la sessió", + "register": "Sincronitza al núvol", + "settings": "Configuració", + "support": "Ajuda" + } + }, + "notFound": { + "badge": "No s'ha trobat", + "goHome": "Torna a l'inici", + "message": "Hem mirat per tot arreu: davall de les papereres, a l'armari, darrere del servidor intermediari, però al remat no hem pogut trobar la pàgina que busqueu.", + "title": "No s'ha pogut trobar la pàgina" + }, + "overlays": { + "close": "Tanca" + }, + "player": { + "back": { + "default": "Torna a l'inici", + "short": "Torna" + }, + "casting": { + "enabled": "S'està emetent al dispositiu…" + }, + "menus": { + "downloads": { + "disclaimer": "Les baixades s'obtenen directament del proveïdor. sudo-flix no té control sobre com es proporcionen les baixades.", + "downloadPlaylist": "Baixa la llista de reproducció", + "downloadSubtitle": "Baixa els subtítols actuals", + "downloadVideo": "Baixa el vídeo", + "hlsDisclaimer": "Les baixades s'obtenen directament del proveïdor. sudo-flix no té control sobre com es proporcionen les baixades. Tingueu en compte que esteu baixant una llista de reproducció HLS, destinada als usuaris familiaritzats amb la transmissió multimèdia avançada.", + "onAndroid": { + "1": "Per a baixar-lo a Android, feu clic al botó de baixada i, a la pàgina nova, manteniu premut el vídeo i, a continuació, seleccioneu Desa.", + "shortTitle": "Baixa / Android", + "title": "Baixada a Android" + }, + "onIos": { + "1": "Per a baixar a iOS, feu clic al botó de baixada i, a la pàgina nova, feu clic a /> i, a continuació, Desa als Arxius .", + "shortTitle": "Baixa / iOS", + "title": "Baixada a iOS" + }, + "onPc": { + "1": "En un PC, feu clic al botó de baixada i, a la pàgina nova, feu clic amb el botó dret al vídeo i seleccioneu Anomena i desa el vídeo", + "shortTitle": "Baixa / PC", + "title": "Baixada a un PC" + }, + "title": "Baixa" + }, + "episodes": { + "button": "Episodi", + "emptyState": "No hi ha episodis en aquesta temporada, torneu-ho a comprovar més tard!", + "episodeBadge": "E{{episode}}", + "loadingError": "Error en carregar la temporada", + "loadingList": "S'està carregant…", + "loadingTitle": "S'està carregant…", + "unairedEpisodes": "Un o més episodis d'aquesta temporada s'han desactivat perquè encara no s'han emés." + }, + "playback": { + "speedLabel": "Velocitat de la reproducció", + "title": "Configuració de la reproducció" + }, + "quality": { + "automaticLabel": "Qualitat automàtica", + "hint": "Podeu provar a <0>canviar la font per a obtenir diferents opcions de qualitat.", + "iosNoQuality": "A causa de les limitacions definides per Apple, la selecció de qualitat no està disponible a iOS per a aquesta font. Podeu provar a <0>canviar a una altra font per a obtenir diferents opcions de qualitat.", + "title": "Qualitat" + }, + "settings": { + "downloadItem": "Baixa", + "enableSubtitles": "Activa els subtítols", + "experienceSection": "Experiència de visualització", + "playbackItem": "Configuració de la reproducció", + "qualityItem": "Qualitat", + "sourceItem": "Fonts de vídeo", + "subtitleItem": "Configuració dels subtítols", + "videoSection": "Configuració de vídeo" + }, + "sources": { + "failed": { + "text": "S'ha produït un error en intentar trobar vídeos, proveu una font diferent.", + "title": "No s'ha pogut obtenir" + }, + "noEmbeds": { + "text": "No hem pogut trobar cap incrustat, proveu una font diferent.", + "title": "No s'ha trobat cap incrustació" + }, + "noStream": { + "text": "Aquesta font no té fluxos per a aquesta pel·lícula o programa.", + "title": "Cap flux" + }, + "title": "Fonts", + "unknownOption": "Desconeguda" + }, + "subtitles": { + "customChoice": "Selecciona un fitxer de subtítols", + "customizeLabel": "Personalitza", + "offChoice": "Desactivats", + "settings": { + "backlink": "Subtítols personalitzats", + "delay": "Retard dels subtítols", + "fixCapitals": "Corregeix les majúscules/minúscules" + }, + "title": "Subtítols", + "unknownLanguage": "Desconeguda" + } + }, + "metadata": { + "api": { + "text": "No s'han pogut carregar les metadades de l'API, comproveu la connexió a Internet.", + "title": "No s'han pogut carregar les metadades de l'API" + }, + "failed": { + "badge": "Ha fallat", + "homeButton": "Vés a l'inici", + "text": "No s'han pogut carregar les metadades del contingut des de TMDB. Comproveu si TMDB no funciona o està bloquejat a la vostra connexió a Internet.", + "title": "Ha fallat la càrrega de les metadades" + }, + "notFound": { + "badge": "No s'ha trobat", + "homeButton": "Torna a l'inici", + "text": "No hem pogut trobar el contingut sol·licitat. O bé s'ha eliminat o bé heu alterat l'URL.", + "title": "No s'ha trobat el contingut." + } + }, + "nextEpisode": { + "cancel": "Cancel·la", + "next": "Episodi següent" + }, + "playbackError": { + "badge": "Error en la reproducció", + "errors": { + "errorAborted": "S'ha interromput l'obtenció del contingut per petició de l'usuari.", + "errorDecode": "Tot i haver-se determinat prèviament que era utilitzable, s'ha produït un error en intentar descodificar el recurs multimèdia.", + "errorGenericMedia": "S'ha produït un error desconegut al contingut.", + "errorNetwork": "S'ha produït algun tipus d'error de xarxa que ha impedit que el contingut s'obtinga correctament, tot i haver estat disponibles prèviament.", + "errorNotSupported": "El contingut o el proveïdor del contingut no és compatible." + }, + "homeButton": "Torna a l'inici", + "text": "S'ha produït un error en intentar reproduir el contingut. Torneu-ho a provar.", + "title": "No s'ha pogut reproduir el vídeo!" + }, + "scraping": { + "items": { + "failure": "S'ha produït un error", + "notFound": "No té el vídeo", + "pending": "S'estan cercant vídeos…" + }, + "notFound": { + "badge": "No s'ha trobat", + "detailsButton": "Mostra els detalls", + "homeButton": "Torna a l'inici", + "text": "Hem buscat a través dels nostres proveïdors i no trobem el contingut que busqueu! No allotgem contingut i no tenim control sobre què hi ha disponible. Feu clic a «Mostra els detalls» a continuació per a més informació.", + "title": "No s'ha pogut trobar" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} restants • Acaba a les {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Verifiqueu que sou humà completant el Captcha de la dreta. Ho fem per a mantenir segura sudo-flix!", + "error": "No s'ha pogut verificar la humanitat. Torneu-ho a provar.", + "title": "Necessitem verificar que sou humà.", + "verifyingHumanity": "Verificant la vostra humanitat…" + } + }, + "screens": { + "dmca": { + "text": "Us donem la benvinguda a la pàgina de contacte DMCA de sudo-flix! Respectem els drets de propietat intel·lectual i volem resoldre qualsevol problema de drets d'autor ràpidament. Si creieu que la vostra obra protegida per drets d'autor s'ha utilitzat incorrectament a la nostra plataforma, envieu un avís detallat de la DMCA al correu electrònic següent. Incloeu una descripció del material protegit per drets d'autor, les vostres dades de contacte i una declaració de creença de bona fe. Ens comprometem a resoldre aquests assumptes amb agilitat i agraïm la vostra col·laboració per a mantenir sudo-flix en un lloc que respecta la creativitat i els drets d'autor.", + "title": "DMCA" + }, + "loadingApp": "S'està carregant l'aplicació", + "loadingUser": "S'està carregant el perfil", + "loadingUserError": { + "logout": "Tanca la sessió", + "reset": "Restableix el servidor personalitzat", + "text": "Ha fallat la càrrega del perfil", + "textWithReset": "Error en carregar el vostre perfil des del servidor personalitzat, voleu restablir el servidor per defecte?" + }, + "migration": { + "failed": "La migració de les dades ha fallat.", + "inProgress": "Espereu, estem migrant les vostres dades. No hauria de tardar massa." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nom del dispositiu", + "deviceNamePlaceholder": "Telèfon personal", + "editProfile": "Edita", + "logoutButton": "Tanca la sessió" + }, + "actions": { + "delete": { + "button": "Elimina el compte", + "confirmButton": "Elimina el compte", + "confirmDescription": "Segur que voleu eliminar el compte? Es perdran totes les dades!", + "confirmTitle": "Segur?", + "text": "Aquesta acció és irreversible. Totes les dades s'eliminaran i no es podrà recuperar res.", + "title": "Elimina el compte" + }, + "title": "Accions" + }, + "devices": { + "deviceNameLabel": "Nom del dispositiu", + "failed": "La càrrega de sessions ha fallat", + "removeDevice": "Elimina", + "title": "Dispositiu" + }, + "profile": { + "finish": "Finalitza l'edició", + "firstColor": "Color de perfil 1", + "secondColor": "Color de perfil dos", + "title": "Edita la foto de perfil", + "userIcon": "Icona d'usuari" + }, + "register": { + "cta": "Comença", + "text": "Compartiu el progrés de la visualització entre dispositius i manteniu-los sincronitzats.", + "title": "Sincronització amb el núvol" + }, + "title": "Compte" + }, + "appearance": { + "activeTheme": "Actiu", + "themes": { + "blue": "Blau", + "default": "Per defecte", + "gray": "Gris", + "red": "Vermell", + "teal": "Verd blavós" + }, + "title": "Aparença" + }, + "connections": { + "server": { + "description": "Si voleu connectar-vos a un rerefons personalitzat per a emmagatzemar les vostres dades, activeu-ho i proporcioneu l'URL. <0>Instruccions.", + "label": "Servidor personalitzat", + "urlLabel": "URL del servidor personalitzat" + }, + "title": "Connexions", + "workers": { + "addButton": "Afig un «worker»", + "description": "Per fer funcionar l'aplicació, tot el trànsit s'encamina a través de servidors intermediaris. Activeu-ho si voleu portar els vostres propis «workers».<0>Instruccions.", + "emptyState": "Encara no hi ha «workers», afegiu-ne un a continuació", + "label": "Utilitza «workers» intermediaris personalitzats", + "urlLabel": "URL dels «workers»", + "urlPlaceholder": "https://" + } + }, + "reset": "Restableix", + "save": "Desa", + "sidebar": { + "info": { + "appVersion": "Versió de l'aplicació", + "backendUrl": "URL del rerefons", + "backendVersion": "Versió del rerefons", + "hostname": "Nom de l'amfitrió", + "insecure": "Insegur", + "notLoggedIn": "No heu iniciat sessió", + "secure": "Segur", + "title": "Informació de l'aplicació", + "unknownVersion": "Desconeguda", + "userId": "ID d'usuari" + } + }, + "subtitles": { + "backgroundLabel": "Opacitat del fons", + "colorLabel": "Color", + "previewQuote": "No he de tenir por. La por és l'assassina de la ment.", + "textSizeLabel": "Grandària del text", + "title": "Subtítols" + }, + "unsaved": "Hi ha canvis sense desar" + } +} diff --git a/src/assets/locales/cs.json b/src/assets/locales/cs.json new file mode 100644 index 00000000..a015e352 --- /dev/null +++ b/src/assets/locales/cs.json @@ -0,0 +1,526 @@ +{ + "about": { + "description": "sudo-flix je webová aplikace, která vyhledává na internetu proudy médií. Cílem týmu je převážně minimalistický přístup ke konzumaci obsahu.", + "faqTitle": "Často kladené otázky", + "q1": { + "body": "sudo-flix nehostuje žádný obsah. Když kliknete na něco, co chcete sledovat, na internetu se vyhledá vybrané médium (Na obrazovce načítání a na kartě 'zdroje videa' můžete vidět, který zdroj používáte). Média se nikdy nenahrávají sudo-flixem, vše probíhá prostřednictvím tohoto vyhledávacího mechanismu.", + "title": "Kde bereme obsah?" + }, + "q2": { + "body": "Není možné požádat o pořad nebo film, sudo-flix nespravuje žádný obsah. Veškerý obsah je prohlížen prostřednictvím zdrojů na internetu.", + "title": "Kde můžu požádat o pořad nebo film?" + }, + "q3": { + "body": "Naše výsledky vyhledávání jsou založeny na The Movie Database (TMDB) a zobrazují se bez ohledu na to, zda naše zdroje skutečně obsah mají.", + "title": "Ve výsledcích vyhledávání se zobrazuje pořad nebo film, proč jej nemůžu přehrát?" + }, + "title": "O sudo-flixu" + }, + "actions": { + "copied": "Zkopírováno", + "copy": "Zkopírovat" + }, + "auth": { + "createAccount": "Ještě nemáte účet? <0>Vytvořte si účet.", + "deviceNameLabel": "Název zařízení", + "deviceNamePlaceholder": "Osobní telefon", + "generate": { + "description": "Vaše přístupová fráze se chová jako vaše přezdívka a heslo. Uchovejte jí v bezpečí, protože jí budete muset zadat, abyste se mohli přihlásit ke svému účtu", + "next": "Uložil jsem si moji přístupovou frázi", + "passphraseFrameLabel": "Přístupová fráze", + "title": "Vaše přístupová fráze" + }, + "hasAccount": "Již máte účet? <0> Přihlaste se zde.", + "login": { + "description": "Pro přihlášení ke svému účtu zadejte svou přístupovou frázi", + "deviceLengthError": "Zadejte název zařízení", + "passphraseLabel": "12slovná přístupová fráze", + "passphrasePlaceholder": "Přístupová fráze", + "submit": "Přihlásit", + "title": "Přihlaste se ke svému účtu", + "validationError": "Nesprávná nebo neúplná přístupová fráze" + }, + "register": { + "information": { + "color1": "První barva profilu", + "color2": "Druhá barva profilu", + "header": "Zadejte název pro vaše zařízení a vyberte barvy a ikonu uživatele podle vašeho výběru", + "icon": "Ikona uživatele", + "next": "Další", + "title": "Informace o účtu" + } + }, + "trust": { + "failed": { + "text": "Nastavili jste to správně?", + "title": "Selhalo připojení k serveru" + }, + "host": "Připojujete se k <0>{{hostname}} - potvrďte, že mu věříte před vytvořením účtu", + "no": "Zpět", + "title": "Věříte tomuto serveru?", + "yes": "Věřím tomuto serveru" + }, + "verify": { + "description": "Zadejte prosím svou přístupovou frázi, abyste potvrdili, že jste si ji uložili, a vytvořte si účet", + "invalidData": "Data nejsou platná", + "noMatch": "Přístupová fráze neodpovídá", + "passphraseLabel": "Vaše 12slovná přístupová fráze", + "recaptchaFailed": "ReCaptcha ověření se nezdařilo", + "register": "Založit účet", + "title": "Potvrďte vaši přístupovou frázi" + } + }, + "errors": { + "badge": "Rozbilo se to", + "details": "Detaily chyby", + "reloadPage": "Znovu načíst stránku", + "showError": "Ukázat detaily chyby", + "title": "Narazili jsme na chybu!" + }, + "footer": { + "legal": { + "disclaimer": "Zřeknutí odpovědnosti", + "disclaimerText": "sudo-flix nehostuje žádné soubory, pouze odkazuje na služby třetích stran. Právní záležitosti by měly být řešeny s hostiteli souborů a poskytovateli. sudo-flix nenese odpovědnost za žádné mediální soubory zobrazené poskytovateli videa." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Sledujte své oblíbené pořady a filmy s touto aplikací pro streamování s otevřeným zdrojovým kódem." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "O nás", + "dmca": "DMCA", + "login": "Přihlásit se", + "onboarding": "Nastavení", + "pagetitle": "{{title}} - sudo-flix", + "register": "Zaregistrovat se", + "settings": "Nastavení" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Záložky" + }, + "continueWatching": { + "sectionTitle": "Pokračujte ve sledování" + }, + "mediaList": { + "stopEditing": "Přestat upravovat" + }, + "search": { + "allResults": "To je vše co máme!", + "failed": "Nepodařilo se najít média, zkuste to znovu!", + "loading": "Načítání...", + "noResults": "Nemohli jsme nic najít!", + "placeholder": "Co si přejete sledovat?", + "sectionTitle": "Výsledky vyhledávání" + }, + "titles": { + "day": { + "default": "Na co byste se chtěli dnes odpoledne dívat?", + "extra": ["Chceš zažít dobrodružství? Jurský Park je pro tebe."] + }, + "morning": { + "default": "Na co byste se chtěli dnes ráno dívat?", + "extra": ["Slyšel jsem, že Před úsvitem je super."] + }, + "night": { + "default": "Na co byste se chtěli dnes večer dívat?", + "extra": ["Unaven? Slyšel jsem, že Vymítač ďábla je super."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Seriál" + } + }, + "navigation": { + "banner": { + "offline": "Zkontrolujte své internetové připojení" + }, + "menu": { + "about": "O nás", + "donation": "Přispět", + "logout": "Odhlásit se", + "register": "Synchronizovat do cloudu", + "settings": "Nastavení", + "support": "Podpořte nás" + } + }, + "notFound": { + "badge": "Nenalezeno", + "goHome": "Zpátky domů", + "message": "Dívali jsme se všude: pod koši, ve skříni, za proxy, ale nakonec jsme nemohli najít stránku, kterou hledáte.", + "title": "Tuto stránku se nepodařilo najít" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Zrušit", + "confirm": "Použít výchozí nastavení", + "description": "Výchozí nastavení nemá nejlepší streamy a může být strašně pomalá.", + "title": "Jste si jist?" + }, + "extension": { + "back": "Zpět", + "explainer": "Pomocí rozšíření prohlížeče můžete získat nejlepší streamy, které nabízíme. S pouhou instalací.", + "extensionHelp": "Pokud jste rozšíření nainstalovali, ale nebylo zjištěno. Otevřete rozšíření pomocí nabídky rozšíření ve vašem prohlížeči a postupujte podle pokynů na obrazovce.", + "status": { + "disallowed": "Rozšíření není pro tuto stránku povoleno", + "disallowedAction": "Povolit rozšíření", + "failed": "Nezdařilo se získávání stavu", + "loading": "Čekání na instalaci rozšíření", + "outdated": "Verze rozšíření je příliš stará", + "success": "Rozšíření funguje podle očekávání!" + }, + "submit": "Pokračovat", + "title": "Začněme s rozšířením" + }, + "proxy": { + "back": "Zpět", + "explainer": "Pomocí metody proxy můžete získat streamy ve skvělé kvalitě vytvořením proxy serveru.", + "input": { + "errorConnection": "Nelze se připojit k proxy", + "errorInvalidUrl": "Adresa URL není platná", + "errorNotProxy": "Byla očekávaná proxy, ale byla předána webová stránka", + "label": "Proxy URL", + "placeholder": "https://" + }, + "link": "Naučit se vytvořit proxy", + "submit": "Předložit proxy", + "title": "Pojďme uďelat novou proxy" + }, + "start": { + "explainer": "Abyste získali co nejlepší streamy. Musíte si zvolit, kterou streamovací metodu chcete použít.", + "options": { + "default": { + "text": "Nechci zdroje s dobrou kvalitou,<0 /> <1>použít výchozí nastavení" + }, + "extension": { + "action": "Nainstalujte si rozšíření", + "description": "Nainstalujte si rozšíření prohlížeče a získejte přístup k nejlepším zdrojům.", + "quality": "Nejlepší kvalita", + "title": "Rozšíření prohlížeče" + }, + "proxy": { + "action": "Nastavit proxy", + "description": "Nastavte si proxy během 5 minut, pro získání přístupu k skvělým zdrojům.", + "quality": "Dobrá kvalita", + "title": "Vlastní proxy" + } + }, + "title": "Pojďme vám nastavit sudo-flix" + } + }, + "overlays": { + "close": "Zavřít" + }, + "player": { + "back": { + "default": "Zpátky domů", + "short": "Zpět" + }, + "casting": { + "enabled": "Odesílání do zařízení..." + }, + "menus": { + "downloads": { + "disclaimer": "Stahování probíhá přímo u poskytovatele. sudo-flix nemá kontrolu nad tím, jak jsou stahování poskytovány.", + "downloadPlaylist": "Stáhnout playlist", + "downloadSubtitle": "Stáhnout aktuální titulky", + "downloadVideo": "Stáhnout video", + "hlsDisclaimer": "Stahování probíhá přímo u poskytovatele. sudo-flix nemá kontrolu nad tím, jak jsou stahování poskytovány.

Vezměte prosím na vědomí, že stahujete HLS playlist, který není doporučen stahovat pokud nejste obeznámeni s pokročilým streamováním médií. Raději skuste jiný zdroj pro jiný formát.", + "onAndroid": { + "1": "Na Androidu klikněte na tlačítko stahování, poté na nové stránce klepněte a podržte na videu a poté vyberte uložit.", + "shortTitle": "Stahování / Android", + "title": "Stahování na Androidu" + }, + "onIos": { + "1": "Na iOS klikněte na tlačítko stahování a poté na nové stránce klikněte na a poté na Uložit do souborů .", + "shortTitle": "Stahování / iOS", + "title": "Stahování na iOS" + }, + "onPc": { + "1": "Na počítači klikněte na tlačítko stahování, poté na nové stránce klikněte pravým tlačítkem na video a vyberte Uložit video jako", + "shortTitle": "Stahování / počítač", + "title": "Stahování na počítači" + }, + "title": "Stáhnout" + }, + "episodes": { + "button": "Epizody", + "emptyState": "V této sezóně nejsou žádné epizody, vraťte se později!", + "episodeBadge": "E{{episode}}", + "loadingError": "Chyba při načítání sezóny", + "loadingList": "Načítání...", + "loadingTitle": "Načítání...", + "unairedEpisodes": "Jedna nebo více epizod v této sezóně nejsou dostupné, protože ještě nebyly odvysílány." + }, + "playback": { + "speedLabel": "Rychlost přehrávání", + "title": "Nastavení přehrávání" + }, + "quality": { + "automaticLabel": "Automatická kvalita", + "hint": "Chcete-li získat jinou kvalitu, můžete zkusit <0>přepnout zdroj.", + "iosNoQuality": "Kvůli omezením definovaným společností Apple není pro tento zdroj v iOS k dispozici výběr kvality. Chcete-li získat jinou kvalitu, můžete zkusit <0>přepnout zdroj.", + "title": "Kvalita" + }, + "settings": { + "downloadItem": "Stáhnout", + "enableSubtitles": "Zapnout titulky", + "experienceSection": "Zážitek sledování", + "playbackItem": "Nastavení přehrávání", + "qualityItem": "Kvalita", + "sourceItem": "Zdroje videa", + "subtitleItem": "Nastavení titulků", + "videoSection": "Nastavení videa" + }, + "sources": { + "failed": { + "text": "Při pokusu o nalezení videí došlo k chybě. Zkuste prosím jiný zdroj.", + "title": "Nepodařilo se extrahovat data" + }, + "noEmbeds": { + "text": "Nepodařilo se nám najít žádný vklad, zkuste prosím jiný zdroj.", + "title": "Žádné vklady" + }, + "noStream": { + "text": "Tento zdroj nemá pro tento film nebo pořad žádné proudy média.", + "title": "Žádný proud média" + }, + "title": "Zdroje", + "unknownOption": "Neznámý" + }, + "subtitles": { + "customChoice": "Vybrat titulky ze souboru", + "customizeLabel": "Přizpůsobit", + "offChoice": "Vypnuto", + "settings": { + "backlink": "Vlastní titulky", + "delay": "Posunutí titulků", + "fixCapitals": "Opravit velká písmena" + }, + "title": "Titulky", + "unknownLanguage": "Neznámé" + } + }, + "metadata": { + "api": { + "text": "Metadata API nelze načíst, zkontrolujte prosím vaše připojení k internetu.", + "title": "Nepodařilo se načíst API metadata" + }, + "dmca": { + "badge": "Odstraněno", + "text": "Toto média není dostupné, kvůli oznámení o zastavení šíření nebo nároku na autorská práva.", + "title": "Média byla odstraněna" + }, + "extensionPermission": { + "badge": "Chybí povolení", + "button": "Použít rozšíření", + "text": "Máte rozšíření, ale k jeho použití potřebujeme vaše povolení.", + "title": "Konfigurace rozšíření" + }, + "failed": { + "badge": "Neúspěšný", + "homeButton": "Jít domů", + "text": "Nelze načíst metadata média z TMDB. Zkontrolujte, zda není TMDB nefunkční nebo blokovaný na vašem internetovém připojení.", + "title": "Načtení metadat se nezdařilo" + }, + "notFound": { + "badge": "Nenalezeno", + "homeButton": "Zpátky domů", + "text": "Nemohli jsme najít média o které jste požádali. Buď bylo odstraňeno, nebo jste manipulovali s URL.", + "title": "Nemohli jsme najít Vaše média." + } + }, + "nextEpisode": { + "cancel": "Zrušit", + "next": "Další epizoda" + }, + "playbackError": { + "badge": "Chyba přehrávání", + "errors": { + "errorAborted": "Načítání média bylo přerušeno uživatelem.", + "errorDecode": "Navzdory tomu, že bylo dříve určeno jako použitelné došlo při pokusu o dekódování média k chybě.", + "errorGenericMedia": "Nastala chyba neznámého média.", + "errorNetwork": "Nastala nějaká chyba síťě, která zabránila načtení média, přestože bylo předtím dostupné.", + "errorNotSupported": "Médium nebo poskytovatel média není podporovaný." + }, + "homeButton": "Jít domů", + "text": "Nastala chyba při přehrávání média. Prosíme skuste to znovu.", + "title": "Video se nepodařilo přehrát!" + }, + "scraping": { + "items": { + "failure": "Nastala chyba", + "notFound": "Nemá toto video", + "pending": "Ověřování videí..." + }, + "notFound": { + "badge": "Nenalezeno", + "detailsButton": "Zobrazit podrobnosti", + "homeButton": "Jít domů", + "text": "Prohledali jsme naše poskytovatele a nenašli jsme média, která hledáte! Nehostujeme žádné média a nemáme žádnou kontrolu nad tím, co je k dispozici. Pro více podrobností klikněte níže na 'Zobrazit podrobnosti'.", + "title": "Nedokázali jsme to najít" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} zbývá • Dokončeno v {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Ověřte, prosím, že jste člověk, vyplněním Captchy napravo. Toto je pro zajištění bezpečnosti sudo-flix!", + "error": "Nepodařilo se ověřit vaši lidskost. Zkuste to prosím znovu.", + "title": "Potřebujeme ověřit, zda jste člověk.", + "verifyingHumanity": "Ověřování vaši lidskosti..." + } + }, + "screens": { + "dmca": { + "text": "Vítejte na DMCA kontaktní stránce sudo-flixu! Respektujeme práva duševního vlastnictví a chceme rychle řešit jakékoli problémy s autorským právem. Pokud se domníváte, že vaše dílo chráněné autorskými právy bylo na naší platformě neoprávněně použito, zašlete prosím podrobné oznámení DMCA na níže uvedený e-mail. Uveďte prosím popis materiálu chráněného autorským právem, své kontaktní údaje a prohlášení o dobré víře. Jsme odhodláni tyto záležitosti rychle vyřešit a oceňujeme vaši spolupráci při udržování sudo-flixu jako místa, které respektuje kreativitu a autorská práva.", + "title": "DMCA" + }, + "loadingApp": "Načítání aplikace", + "loadingUser": "Načítání vášeho profilu", + "loadingUserError": { + "logout": "Odhlásit se", + "reset": "Resetovat vlastní server", + "text": "Nezdařilo se načíst váš profil", + "textWithReset": "Nezdařilo se načíst váš profil z vašeho serveru, chcete ho přepnout na výchozí server?" + }, + "migration": { + "failed": "Migrace dat se nezdařila.", + "inProgress": "Počkejte prosím, migrujeme vaše data. Nemělo by to trvat dlouho." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Název zařízení", + "deviceNamePlaceholder": "Osobní telefon", + "editProfile": "Upravit", + "logoutButton": "Odhlásit se" + }, + "actions": { + "delete": { + "button": "Smazat účet", + "confirmButton": "Smazat účet", + "confirmDescription": "Jste si jisti, že chcete smazat váš účet? Všechny data budou ztracena!", + "confirmTitle": "Jste si jisti?", + "text": "Tato akce nejde vrátit. Všechny data budou smazána a nic nepůjde zachránit.", + "title": "Smazat účet" + }, + "title": "Akce" + }, + "devices": { + "deviceNameLabel": "Název zařízení", + "failed": "Načtení relací se nezdařilo", + "removeDevice": "Odstranit", + "title": "Zařízení" + }, + "profile": { + "finish": "Dokončit", + "firstColor": "První barva profilu", + "secondColor": "Druhá barva profilu", + "title": "Upravit profilovou fotografii", + "userIcon": "Ikona uživatele" + }, + "register": { + "cta": "Začněte", + "text": "Sdílejte průběh sledování mezi zařízeními a udržujte je synchronizovaná.", + "title": "Synchronizace do cloudu" + }, + "title": "Účet" + }, + "appearance": { + "activeTheme": "Aktivní", + "themes": { + "blue": "Modrá", + "default": "Výchozí", + "gray": "Šedá", + "red": "Červená", + "teal": "Modrozelená" + }, + "title": "Vzhled" + }, + "connections": { + "server": { + "description": "Pokud se chcete připojit k vlastnímu backendu pro ukládání dat, povolte toto a zadejte URL adresu. <0>Instrukce.", + "label": "Vlastní server", + "urlLabel": "URL adresa vlastního serveru" + }, + "setup": { + "doSetup": "Proveďte nastavení", + "errorStatus": { + "description": "Vypadá to, že jedna nebo více položek v tomto nastavení potřebuje vaši pozornost.", + "title": "Něco potřebuje vaši pozornost" + }, + "itemError": "S tímto nastavením je něco špatně. Projděte znovu nastavením abyste to opravili.", + "items": { + "default": "Výchozí nastavení", + "extension": "Rozšíření", + "proxy": "Vlastní proxy" + }, + "redoSetup": "Proveďte znovu nastavení", + "successStatus": { + "description": "Všechny věci jsou připraveny, abyste mohli začít sledovat svá oblíbená média.", + "title": "Všechno je nastaveno!" + }, + "unsetStatus": { + "description": "Prosím klikněte na vedlejší tlačítko abyste začali proces nastavování.", + "title": "Neprošli jste nastavením" + } + }, + "title": "Spojení", + "workers": { + "addButton": "Přidat nového pracovníka", + "description": "Aby byla aplikace funkční, veškerá trafika prochází přes proxy. Povolte toto, pokud chcete používat svoje vlastní pracovníky. <0>Instrukce.", + "emptyState": "Zatím žádní pracovníci, přidej jednoho dolů", + "label": "Použít vlastní proxy pracovníky", + "urlLabel": "URL adresy pracovníků", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Jazyk aplikace", + "languageDescription": "Jazyk aplikován na celou aplikaci.", + "thumbnail": "Generovat miniatury", + "thumbnailDescription": "Videa většinou nemají miniatury. Toto nastavení můžete povolit, ale mohou zpomalit vaše video.", + "thumbnailLabel": "Generovat miniatury", + "title": "Preference" + }, + "reset": "Resetovat", + "save": "Uložit", + "sidebar": { + "info": { + "appVersion": "Verze aplikace", + "backendUrl": "URL backendu", + "backendVersion": "Verze backendu", + "hostname": "Název hostitele", + "insecure": "nebezpečný", + "notLoggedIn": "Nejste přihlášen", + "secure": "bezpečný", + "title": "Informace o aplikaci", + "unknownVersion": "Neznámo", + "userId": "Uživatelské ID" + } + }, + "subtitles": { + "backgroundLabel": "Neprůhlednost pozadí", + "colorLabel": "Barva", + "previewQuote": "Nesmím se bát. Strach zabíjí myšlení.", + "textSizeLabel": "Velikost textu", + "title": "Titulky" + }, + "unsaved": "Máte neuložené změny" + } +} diff --git a/src/assets/locales/de.json b/src/assets/locales/de.json new file mode 100644 index 00000000..968a808f --- /dev/null +++ b/src/assets/locales/de.json @@ -0,0 +1,482 @@ +{ + "about": { + "description": "sudo-flix ist eine Web-App, welche das Internet nach Streams durchsucht. Das Team versucht einen minimalistischen Ansatz umzusetzen.", + "faqTitle": "Häufig gestellte Fragen", + "q1": { + "body": "sudo-flix 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). sudo-flix lädt keine Videos hoch, alleVideos stammen aus der Suche.", + "title": "Woher stammen die Videos?" + }, + "q2": { + "body": "Das Anfragen von Serien oder Filmen ist nicht möglich. sudo-flix 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, egal ob unsere Videoquellen über dieses Video verfügen, angezeigt.", + "title": "Die Suche zeigt eine Serie oder einen Film an, warum kann ich den dann nicht abspielen?" + }, + "title": "Über sudo-flix" + }, + "actions": { + "copied": "Kopiert", + "copy": "Kopieren" + }, + "auth": { + "createAccount": "Du hast noch keinen Account? <0>Registriere dich jetzt.", + "deviceNameLabel": "Gerätename", + "deviceNamePlaceholder": "Handy", + "generate": { + "description": "Deine Passphrase dient als dein Nutzername und Passwort. Speichere sie sicher ab, damit du dich in deinem Konto anmelden kannst", + "next": "Ich habe meine Passphrase gespeichert", + "passphraseFrameLabel": "Passphrase", + "title": "Deine Passphrase" + }, + "hasAccount": "Du hast bereits einen Account? <0>Anmelden.", + "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 Symbol", + "icon": "Symbol", + "next": "Weiter", + "title": "Kontoinformationen" + } + }, + "trust": { + "failed": { + "text": "Hast du es korrekt konfiguriert?", + "title": "Server nicht erreichbar" + }, + "host": "Du verbindest dich mit <0>{{hostname}} - stelle sicher das du dem Server vertraust, bevor du ein Konto erstellst", + "no": "Zurück", + "title": "Vertraust du diesem Server?", + "yes": "Ich vertraue diesem Server" + }, + "verify": { + "description": "Bitte gebe deine Passphrase ein, 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": "sudo-flix hostet keine Dateien, sondern verlinkt lediglich auf Dienste Dritter. Rechtliche Fragen sollten mit den Dateihostern und -anbietern geklärt werden. sudo-flix ü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": "sudo-flix", + "pages": { + "about": "Über", + "dmca": "DMCA", + "login": "Anmelden", + "onboarding": "Einrichtung", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registrieren", + "settings": "Einstellungen" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Favoriten" + }, + "continueWatching": { + "sectionTitle": "Weiter ansehen" + }, + "mediaList": { + "stopEditing": "Bearbeiten beenden" + }, + "search": { + "allResults": "Das ist alles!", + "failed": "Das Medium wurde nicht gefunden, bitte versuchen Sie es erneut!", + "loading": "Wird geladen...", + "noResults": "Wir haben nichts gefunden!", + "placeholder": "Was möchtest du schauen?", + "sectionTitle": "Suchergebnisse" + }, + "titles": { + "day": { + "default": "Was würdest du diesem Nachmittag gerne schauen?", + "extra": [ + "Lust auf ein Abenteuer? Dann ist Jurassic Park genau das Richtige." + ] + }, + "morning": { + "default": "Was würdest du diesen Morgen gerne schauen?", + "extra": ["Before Sunrise soll gut sein"] + }, + "night": { + "default": "Was möchtest 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" + } + }, + "navigation": { + "banner": { + "offline": "Internetverbindung ist instabil" + }, + "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", + "message": "Wir haben überall gesucht: Unter den Eimern, im Schrank, hinter dem Proxy. Wir konnten die gesuchte Seite nicht finden.", + "title": "Diese Seite wurde nicht gefunden" + }, + "onboarding": { + "proxy": { + "back": "Zurück gehen" + }, + "start": { + "options": { + "extension": { + "quality": "Beste Qualität" + } + } + } + }, + "overlays": { + "close": "Schließen" + }, + "player": { + "back": { + "default": "Zurück zur Startseite", + "short": "Zurück" + }, + "casting": { + "enabled": "Casting zum Gerät..." + }, + "menus": { + "downloads": { + "disclaimer": "Videos werden direkt vom Provider heruntergeladen. sudo-flix hat keinen Einfluss darauf, wie Downloads bereitgestellt werden.", + "downloadPlaylist": "Playlist herunterladen", + "downloadSubtitle": "Aktuelle Untertitel herunterladen", + "downloadVideo": "Video herunterladen", + "hlsDisclaimer": "sudo-flix hat keinen Einfluss darauf, wie Downloads bereitgestellt werden. Downloads werden direkt vom Anbieter bezogen. Bitte beachte, dass du eine HLS-Wiedergabeliste herunterlädst, dies ist nicht empfohlen für Nutzer die nicht mit Erweiterten Streaming Formaten vertraut sind. Probiere andere Anbieter für andere Formate.", + "onAndroid": { + "1": "Um auf Android Herunterzuladen, tippe auf den Download-Button, tippe und halte auf der neuen Seite auf das Video und wähle Speichern 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 , dann auf In Dateien sichern .", + "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 Video speichern als", + "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...", + "unairedEpisodes": "Eine oder mehrere Episoden dieser Staffel wurden deaktiviert, weil sie noch nicht ausgestrahlt wurden." + }, + "playback": { + "speedLabel": "Wiedergabegeschwindigkeit", + "title": "Wiedergabeeinstellungen" + }, + "quality": { + "automaticLabel": "Automatische Qualitätseinstellung", + "hint": "Du kannst versuchen die <0>Quelle zu ändern 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 um andere Qualitätsoptionen zu erhalten.", + "title": "Qualität" + }, + "settings": { + "downloadItem": "Download", + "enableSubtitles": "Untettitel aktivieren", + "experienceSection": "Anzeigeerlebnis", + "playbackItem": "Wiedergabeeinstellungen", + "qualityItem": "Qualität", + "sourceItem": "Videoquellen", + "subtitleItem": "Untertiteleinstellungen", + "videoSection": "Videoeinstellungen" + }, + "sources": { + "failed": { + "text": "Beim Versuch, Videos zu finden, ist ein Fehler aufgetreten. Bitte versuche es mit einer anderen Quelle.", + "title": "Fehler beim Laden" + }, + "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" + }, + "subtitles": { + "customChoice": "Untertitel aus Datei wählen", + "customizeLabel": "Anpassen", + "offChoice": "Aus", + "settings": { + "backlink": "Eigene Untertitel", + "delay": "Untertitelverzögerung", + "fixCapitals": "Großschreibung korrigieren" + }, + "title": "Untertitel", + "unknownLanguage": "Unbekannt" + } + }, + "metadata": { + "api": { + "text": "API Metadaten konnten nicht geladen werden, überprüfe deine Netzwerkverbindung.", + "title": "API Metadaten konnten nicht geladen werden" + }, + "dmca": { + "badge": "Entfernt", + "text": "Das Video ist aufgrund einer Takedown-Anfrage oder eines Urheberrechtsanspruchs nicht mehr verfügbar.", + "title": "Video wurde entfernt" + }, + "extensionPermission": { + "badge": "Berechtigung fehlt", + "button": "Verwende Erweiterung", + "text": "Du hast die Browser-Erweiterung, aber wir brauchen deine Berechtigung um mit der Erweiterung zu starten.", + "title": "Konfiguriere die Erweiterung" + }, + "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 Video nicht finden.", + "title": "Das Video 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": "Beim Versuch, das Video zu dekodieren, ist ein Fehler aufgetreten, obwohl sie zuvor als brauchbar eingestuft worden war.", + "errorGenericMedia": "Unbekannter Videofehler ist aufgetreten.", + "errorNetwork": "Es ist ein Netzwerkfehler aufgetreten, der verhindert hat, dass das Video erfolgreich abgerufen werden konnten, obwohl es verfügbar waren.", + "errorNotSupported": "Das Video- oder Videoanbieter wird nicht unterstützt." + }, + "homeButton": "Zurück zur Startseite", + "text": "Während der Wiedergabe ist ein Fehler 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 auf \"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}}" + }, + "turnstile": { + "description": "Bitte verifiziere deine Menschlichkeit mit dem Captcha auf der Rechten Seite. Dies ist um sudo-flix sicher zu halten!", + "error": "Menschlichkeit konnte nicht verifiziert werden. Bitte versuche es erneut.", + "title": "Wir müssen sicherstellen, dass du Menschlich bist.", + "verifyingHumanity": "Verifiziere deine Menschlichkeit..." + } + }, + "screens": { + "dmca": { + "text": "Willkommen zu sudo-flixs 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 sudo-flix 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": "Symbol" + }, + "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" + }, + "connections": { + "server": { + "description": "Falls du dich mit einem anderen Server verbinden willst, um deine Daten zu speichern. Aktiviere dies und gebe die URL an. <0>Anweisungen.", + "label": "Eigener Server", + "urlLabel": "Eigene Server-URL" + }, + "setup": { + "doSetup": "Einrichten", + "errorStatus": { + "description": "Es scheint so, als bräuchte eine oder mehrere Schritte in dieser Einrichtung deine Aufmerksamkeit.", + "title": "Etwas braucht deine Aufmerksamkeit" + }, + "itemError": "Mit diesem Setup stimmt etwas nicht. Führen Sie das Setup erneut durch, um das Problem zu beheben.", + "items": { + "default": "Standard Einstellungen", + "extension": "Erweiterung", + "proxy": "Eigene Proxy" + }, + "redoSetup": "Erneut einrichten", + "successStatus": { + "description": "Alles ist bereit, damit du deine Lieblingsserien und Filme ansehen kannst.", + "title": "Alles eingerichtet!" + }, + "unsetStatus": { + "description": "Klicken Sie bitte auf den Button rechts, um die Setup zu starten.", + "title": "Du hast das Setup noch nicht durchgeführt" + } + }, + "title": "Verbindung", + "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. <0>Anweisungen.", + "emptyState": "Keine Worker vorhanden, füge einen unten hinzu", + "label": "Verwenden deinen eigenen Worker-Proxys", + "urlLabel": "Worker-URLs", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "App-Sprache", + "languageDescription": "Sprache für die ganze App.", + "thumbnail": "Miniaturansichten generieren", + "thumbnailDescription": "Meistens haben Videos keine Miniaturansicht. Du kannst diese Einstellung anmachen, um sie automatisch zu generieren, aber Sie können dein Video langsamer machen.", + "thumbnailLabel": "Miniaturansichten generieren", + "title": "Einstellungen" + }, + "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" + } + }, + "subtitles": { + "backgroundLabel": "Hintergrunddeckkraft", + "colorLabel": "Farbe", + "previewQuote": "Das Gras wächst nicht schneller, wenn man daran zieht.", + "textSizeLabel": "Schriftgröße", + "title": "Untertitel" + }, + "unsaved": "Du hast ungespeicherte Änderungen" + } +} diff --git a/src/assets/locales/el.json b/src/assets/locales/el.json new file mode 100644 index 00000000..21737dfe --- /dev/null +++ b/src/assets/locales/el.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "Το sudo-flix είναι μία διαδικτυακή εφαρμογή που αναζητά το ίντερνετ για streams. Η ομάδα επιδιώκει μία κυρίως μινιμαλιστική προσέγγιση στην κατανάλωση περιεχομένου.", + "faqTitle": "Συχνές ερωτήσεις", + "q1": { + "body": "Το sudo-flix δεν φιλοξενεί περιεχόμενο. Σε κάθε κλικ για οτιδήποτε θέλετε να παρακολουθήσετε, το sudo-flix πραγματοποιεί αναζήτηση στο διαδίκτυο για το επιλεγμένο πολυμέσο (Στην οθόνη φόρτωσης και στην καρτέλα \"πηγές βίντεο\" μπορείτε να δείτε ποια πηγή χρησιμοποιείτε). Τα πολυμέσα δεν φορτώνονται ποτέ από το sudo-flix, όλα γίνονται μέσω του παραπάνω μηχανισμού.", + "title": "Από πού προέρχεται το περιεχόμενο;" + }, + "q2": { + "body": "Δεν είναι εφικτό να αιτηθούν σειρές ή ταινίες, το sudo-flix δεν διαχειρίζεται το περιεχόμενο. Όλα τα πολυμέσα προβάλονται από πηγές στο διαδίκτυο.", + "title": "Που μπορώ να αιτηθώ μία σειρά ή ταινία;" + }, + "q3": { + "body": "Τα αποτελέσματα των αναζητήσεων τροφοδοτούνται από το The Movie Database (TMDB) και προβάλλονται ανεξάρτητα με το αν οι πηγές διαθέτουν το εν λόγω πολυμέσο.", + "title": "Τα αποτελέσματα της αναζήτησης εμφανίζουν την σειρά ή την ταινία, γιατί δεν μπορώ να την αναπαράγω;" + }, + "title": "Σχετικά με το sudo-flix" + }, + "actions": { + "copied": "Αντιγράφθηκε", + "copy": "Αντιγραφή" + }, + "auth": { + "createAccount": "Δεν έχετε ακόμη λογαριασμό; <0>Δημιουργήστε ένα λογαριασμό.", + "deviceNameLabel": "Όνομα συσκευής", + "deviceNamePlaceholder": "Προσωπικό τηλέφωνο", + "generate": { + "description": "Η συνθηματική σας φράση λειτουργεί ταυτόχρονα ως όνομα χρήστη και κωδικός πρόσβασης. Φροντίστε να το φυλάξετε, καθώς θα χρειαστεί να το εισάγετε για να συνδεθείτε στο λογαριασμό σας", + "next": "Έχω αποθηκέυση την συνθηματική μου φράση", + "passphraseFrameLabel": "Συνθηματική φράση", + "title": "Η συνθηματική σας φράση" + }, + "hasAccount": "Έχετε ήδη λογαριασμό; <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}} - παρακαλούμε επιβεβαιώστε ότι τον εμπιστεύεστε πριν δημιουργήσετε λογαριασμό", + "no": "Επιστροφή", + "title": "Εμπιστεύεστε αυτόν τον διακομιστή;", + "yes": "Εμπιστεύομαι αυτόν τον διακομιστή" + }, + "verify": { + "description": "Παρακαλώ εισάγετε τη συνθηματική σας φράση από τα προηγούμενα βήματα για να επιβεβαιώσετε ότι την έχετε αποθηκεύσει και να δημιουργήσετε τον λογαριασμό σας", + "invalidData": "Τα δεδομένα δεν είναι έγκυρα", + "noMatch": "Η φράση πρόσβασης δεν ταυτίζεται", + "passphraseLabel": "Η συνθηματική σας φράση 12 λέξεων", + "recaptchaFailed": "Η επικύρωση του ReCaptcha απέτυχε", + "register": "Δημιουργία λογαριασμού", + "title": "Επικυρώστε τη συνθηματική σας φράση" + } + }, + "errors": { + "badge": "Χάλασε", + "details": "Λεπτομέρειες σφάλματος", + "reloadPage": "Επαναφόρτωση της σελίδας", + "showError": "Εμφάνιση λεπτομερειών σφάλματος", + "title": "Αντιμετωπίσαμε ένα σφάλμα!" + }, + "footer": { + "legal": { + "disclaimer": "Αποποίηση ευθύνης", + "disclaimerText": "Το sudo-flix δεν φιλοξενεί κανένα αρχείο, απλώς παραπέμπει σε υπηρεσίες τρίτων. Τα νομικά ζητήματα θα πρέπει να διευθετηθούν με τους παρόχους και τους διακομιστές που φιλοξενούν τα εν λόγω αρχεία. Το sudo-flix δεν είναι υπεύθυνο για τα αρχεία πολυμέσων που προβάλλονται από τους παρόχους βίντεο." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA (νόμος περί πνευματικών δικαιωμάτων)", + "github": "GitHub" + }, + "tagline": "Παρακολουθήστε τις αγαπημένες σας σειρές και ταινίες με αυτή την εφαρμογή streaming ανοιχτού κώδικα." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Σχετικά", + "dmca": "DMCA (νόμος περί πνευματικών δικαιωμάτων)", + "login": "Σύνδεση", + "pagetitle": "{{title}} - sudo-flix", + "register": "Εγγραφή", + "settings": "Ρυθμίσεις" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Σελιδοδείκτες" + }, + "continueWatching": { + "sectionTitle": "Συνεχίστε να παρακολουθείτε" + }, + "mediaList": { + "stopEditing": "Σταματήστε την επεξεργασία" + }, + "search": { + "allResults": "Αυτά είναι όλα όσα έχουμε!", + "failed": "Απέτυχε η εύρεση πολυμέσων, δοκιμάστε ξανά!", + "loading": "Φόρτωση...", + "noResults": "Δεν μπορέσαμε να βρούμε τίποτα!", + "placeholder": "Τι θέλετε να παρακολουθήσετε;", + "sectionTitle": "Αποτελέσματα αναζήτησης" + }, + "titles": { + "day": { + "default": "Τι θα θέλατε να παρακολουθήσετε σήμερα το απόγευμα;", + "extra": [ + "Νιώθετε περιπετειώδης; Το Jurassic Park μπορεί να είναι η τέλεια επιλογή." + ] + }, + "morning": { + "default": "Τι θα θέλατε να παρακολουθήσετε σήμερα το πρωί;", + "extra": ["Έχω ακούσει ότι το Before Sunrise είναι καλό"] + }, + "night": { + "default": "Τι θα θέλατε να παρακολουθήσετε απόψε;", + "extra": ["Κούραση; Έχω ακούσει ότι ο Εξορκιστής είναι καλός."] + } + } + }, + "media": { + "episodeDisplay": "Σ{{season}} Ε{{episode}}", + "types": { + "movie": "Ταινία", + "show": "Σειρά" + } + }, + "navigation": { + "banner": { + "offline": "Ελέγξτε τη σύνδεσή σας στο διαδίκτυο" + }, + "menu": { + "about": "Σχετικά με εμάς", + "donation": "Δωρεά", + "logout": "Αποσύνδεση", + "register": "Συγχρονισμός με το cloud", + "settings": "Ρυθμίσεις", + "support": "Υποστήριξη" + } + }, + "notFound": { + "badge": "Δεν βρέθηκε", + "goHome": "Επιστροφή στην αρχική", + "message": "Ψάξαμε παντού: κάτω από τους κάδους, στην ντουλάπα, πίσω από το proxy, αλλά τελικά δεν μπορέσαμε να βρούμε τη σελίδα που ψάχνετε.", + "title": "Δεν μπόρεσε να βρεθεί η σελίδα" + }, + "overlays": { + "close": "Κλείσιμο" + }, + "player": { + "back": { + "default": "Επιστροφή στην αρχική", + "short": "Επιστροφή" + }, + "casting": { + "enabled": "Αναμετάδοση στην συσκευή..." + }, + "menus": { + "downloads": { + "disclaimer": "Οι λήψεις προέρχονται απευθείας από τον πάροχο. Το sudo-flix δεν ελέγχει τον τρόπο με τον οποίο παρέχονται οι λήψεις.", + "downloadPlaylist": "Λήψη λίστας αναπαραγωγής", + "downloadSubtitle": "Κατέβασμα τρέχοντος υπότιτλου", + "downloadVideo": "Λήψη βίντεο", + "hlsDisclaimer": "Οι λήψεις προέρχονται απευθείας από τον πάροχο. Το sudo-flix δεν ελέγχει τον τρόπο με τον οποίο παρέχονται οι λήψεις. Παρακαλώ λάβετε υπόψη ότι κατεβάζετε μια λίστα αναπαραγωγής HLS, η οποία προορίζεται για χρήστες που είναι εξοικειωμένοι με την προηγμένη ροή πολυμέσων.", + "onAndroid": { + "1": "Για να κάνετε λήψη σε συσκευή Android, κάντε κλικ στο κουμπί λήψης και, στη νέα σελίδα, πατήστε παρατεταμένα στο βίντεο και, στη συνέχεια, επιλέξτε αποθήκευση.", + "shortTitle": "Λήψη / Android", + "title": "Λήψη σε συσκευή Android" + }, + "onIos": { + "1": "Για να κάνετε λήψη σε συσκευή iOS, κάντε κλικ στο κουμπί λήψης και, στη νέα σελίδα, κάντε κλικ στο και, στη συνέχεια, στο Αποθήκευση στα αρχεία .", + "shortTitle": "Λήψη / iOS", + "title": "Λήψη σε συσκευή iOS" + }, + "onPc": { + "1": "Στον υπολογιστή, κάντε κλικ στο κουμπί λήψης και, στη νέα σελίδα, κάντε δεξί κλικ στο βίντεο και επιλέξτε Αποθήκευση βίντεο ως", + "shortTitle": "Λήψη / PC", + "title": "Λήψη στο PC" + }, + "title": "Λήψη" + }, + "episodes": { + "button": "Επεισόδια", + "emptyState": "Δεν υπάρχουν επεισόδια σε αυτή τη σεζόν, επιστρέψτε αργότερα!", + "episodeBadge": "Ε{{episode}}", + "loadingError": "Σφάλμα φόρτωσης της σεζόν", + "loadingList": "Φόρτωση...", + "loadingTitle": "Φόρτωση...", + "unairedEpisodes": "Ένα ή περισσότερα επεισόδια αυτής της σεζόν έχουν απενεργοποιηθεί επειδή δεν έχουν προβληθεί ακόμα." + }, + "playback": { + "speedLabel": "Ταχύτητα αναπαραγωγής", + "title": "Ρυθμίσεις αναπαραγωγής" + }, + "quality": { + "automaticLabel": "Αυτόματη ποιότητα", + "hint": "Μπορείτε να δοκιμάσετε <0>να αλλάξετε πηγή για να έχετε διαφορετικές επιλογές ποιότητας.", + "iosNoQuality": "Λόγω των περιορισμών που έχει ορίσει η Apple, η επιλογή ποιότητας δεν είναι διαθέσιμη σε συσκευές iOS για αυτή την πηγή. Μπορείτε να δοκιμάσετε <0>να αλλάξετε πηγή για να έχετε διαφορετικές επιλογές ποιότητας.", + "title": "Ποιότητα" + }, + "settings": { + "downloadItem": "Λήψη", + "enableSubtitles": "Ενεργοποίηση υποτίτλων", + "experienceSection": "Εμπειρία θέασης", + "playbackItem": "Ρυθμίσεις αναπαραγωγής", + "qualityItem": "Ποιότητα", + "sourceItem": "Πηγές βίντεο", + "subtitleItem": "Ρυθμίσεις υποτίτλων", + "videoSection": "Ρυθμίσεις βίντεο" + }, + "sources": { + "failed": { + "text": "Υπήρξε σφάλμα κατά την προσπάθεια εύρεσης βίντεο, παρακαλούμε δοκιμάστε μια άλλη πηγή.", + "title": "Το scrape απέτυχε" + }, + "noEmbeds": { + "text": "Δεν μπορέσαμε να βρούμε κανένα embed, παρακαλούμε δοκιμάστε μια άλλη πηγή.", + "title": "Δεν βρέθηκε κανένα embed" + }, + "noStream": { + "text": "Αυτή η πηγή δεν έχει streams για αυτή την ταινία ή σειρά.", + "title": "Δεν υπάρχει stream" + }, + "title": "Πηγές", + "unknownOption": "Άγνωστο" + }, + "subtitles": { + "customChoice": "Επιλογή υποτίτλων από αρχείο", + "customizeLabel": "Προσαρμογή", + "offChoice": "Σβηστό", + "settings": { + "backlink": "Προσαρμοσμένοι υπότιτλοι", + "delay": "Καθυστέρηση υποτίτλων", + "fixCapitals": "Επιδιόρθωση κεφαλαιοποίησης" + }, + "title": "Υπότιτλοι", + "unknownLanguage": "Άγνωστο" + } + }, + "metadata": { + "api": { + "text": "Δεν ήταν δυνατή η φόρτωση των μεταδεδομένων του API, ελέγξτε τη σύνδεσή σας στο διαδίκτυο.", + "title": "Αποτυχία φόρτωσης μεταδεδομένων API" + }, + "failed": { + "badge": "Απέτυχε", + "homeButton": "Επιστοφή στην αρχική", + "text": "Δεν ήταν δυνατή η φόρτωση των μεταδεδομένων του πολυμέσου από την TMDB. Ελέγξτε αν η TMDB είναι εκτός λειτουργίας ή αποκλεισμένη στη σύνδεσή σας στο διαδίκτυο.", + "title": "Αποτυχία φόρτωσης μεταδεδομένων" + }, + "notFound": { + "badge": "Δεν βρέθηκε", + "homeButton": "Επιστροφή στην αρχική σελίδα", + "text": "Δεν μπορέσαμε να βρούμε το πολυμέσο που ζητήσατε. Είτε έχει αφαιρεθεί είτε παραποιήσατε τη διεύθυνση URL.", + "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}}" + }, + "turnstile": { + "description": "Επαληθεύστε ότι είστε άνθρωπος συμπληρώνοντας το Captcha στα δεξιά. Αυτό γίνεται για να διατηρηθεί ο ιστός της ταινίας ασφαλής!", + "error": "Αποτυχία επαλήθευσης της ανθρωπιάς σας. ΠΑΡΑΚΑΛΩ προσπαθησε ξανα.", + "title": "Πρέπει να επαληθεύσουμε ότι είστε άνθρωπος.", + "verifyingHumanity": "Επαλήθευση της ανθρωπιάς σας..." + } + }, + "screens": { + "dmca": { + "text": "Καλώς ήρθατε στη σελίδα επικοινωνίας DMCA (νόμου περί πνευματικών δικαιωμάτων) της sudo-flix! Σεβόμαστε τα δικαιώματα πνευματικής ιδιοκτησίας και επιθυμούμε να αντιμετωπίσουμε γρήγορα τυχόν νομικά ζητήματα πνευματικών δικαιωμάτων. Εάν πιστεύετε ότι το έργο σας που προστατεύεται από πνευματικά δικαιώματα έχει χρησιμοποιηθεί αθέμιτα στην πλατφόρμα μας, παρακαλούμε στείλτε μια λεπτομερή ειδοποίηση στην παρακάτω διεύθυνση ηλεκτρονικού ταχυδρομίου. Παρακαλούμε να συμπεριλάβετε μια περιγραφή του υλικού που προστατεύεται από πνευματικά δικαιώματα, τα στοιχεία επικοινωνίας σας και μια δήλωση καλής πίστης. Δεσμευόμαστε να επιλύουμε αυτά τα θέματα άμεσα και εκτιμούμε τη συνεργασία σας για να διατηρήσουμε το sudo-flix ένα μέρος που σέβεται τη δημιουργικότητα και τα πνευματικά δικαιώματα.", + "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": "Συγχρονισμός με το cloud" + }, + "title": "Λογαριασμός" + }, + "appearance": { + "activeTheme": "Ενεργός", + "themes": { + "blue": "Μπλε", + "default": "Προεπιλογή", + "gray": "Γκρι", + "red": "Κόκκινο", + "teal": "Πετρόλ" + }, + "title": "Εμφάνιση" + }, + "connections": { + "server": { + "description": "Αν θέλετε να συνδεθείτε σε ένα προσαρμοσμένο backend για την αποθήκευση των δεδομένων σας, ενεργοποιήστε το και παραχωρήστε τη διεύθυνση URL.", + "label": "Προσαρμοσμένος διακομιστής", + "urlLabel": "Διεύθυνση URL προσαρμοσμένου διακομιστή" + }, + "title": "Συνδέσεις", + "workers": { + "addButton": "Προσθήκη νέου worker", + "description": "Για να λειτουργήσει η εφαρμογή, όλη η κυκλοφορία δρομολογείται μέσω διακομιστών μεσολάβησης. Ενεργοποιήστε το εάν θέλετε να φέρετε τους δικούς σας workers.", + "emptyState": "Δεν υπάρχουν ακόμη workers, προσθέστε έναν παρακάτω", + "label": "Χρήση προσαρμοσμένων proxy workers", + "urlLabel": "Διευθύνσεις URL των worker", + "urlPlaceholder": "https://" + } + }, + "reset": "Επαναφορά", + "save": "Αποθήκευση", + "sidebar": { + "info": { + "appVersion": "Έκδοση εφαρμογής", + "backendUrl": "Διεύθυνση URL του backend", + "backendVersion": "Έκδοση του backend", + "hostname": "Όνομα διακομιστή", + "insecure": "Ανασφαλές", + "notLoggedIn": "Δεν είστε συνδεδεμένοι", + "secure": "Ασφαλές", + "title": "Πληροφορίες εφαρμογής", + "unknownVersion": "Άγνωστο", + "userId": "Αναγνωριστικό χρήστη" + } + }, + "subtitles": { + "backgroundLabel": "Διαφάνεια φόντου", + "colorLabel": "Χρώμα", + "previewQuote": "Δεν πρέπει να φοβάμαι. Ο φόβος είναι ο δολοφόνος του μυαλού.", + "textSizeLabel": "Μέγεθος κειμένου", + "title": "Υπότιτλοι" + }, + "unsaved": "Έχετε μη αποθηκευμένες αλλαγές" + } +} diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json new file mode 100644 index 00000000..2003ef34 --- /dev/null +++ b/src/assets/locales/en.json @@ -0,0 +1,533 @@ +{ + "about": { + "description": "sudo-flix is a web application that searches the internet for streams. The team aims for a mostly minimalistic approach to consuming content.", + "faqTitle": "Common questions", + "q1": { + "body": "sudo-flix does not host any content. When you click on something to watch, the internet is searched for the selected media (On the loading screen and in the 'video sources' tab you can see which source you're using). Media never gets uploaded by sudo-flix, everything is through this searching mechanism.", + "title": "Where does the content come from?" + }, + "q2": { + "body": "It's not possible to request a show or movie, sudo-flix does not manage any content. All content is viewed through sources on the internet.", + "title": "Where can I request a show or movie?" + }, + "q3": { + "body": "Our search results are powered by The Movie Database (TMDB) and display regardless of whether our sources actually have the content.", + "title": "The search results display the show or movie, why can't I play it?" + }, + "title": "About sudo-flix" + }, + "actions": { + "copied": "Copied", + "copy": "Copy" + }, + "auth": { + "createAccount": "Don't have an account yet? <0>Create an account.", + "deviceNameLabel": "Device name", + "deviceNamePlaceholder": "Personal phone", + "generate": { + "description": "Your passphrase acts as your username and password. Make sure to keep it safe as you will need to enter it to login to your account", + "next": "I have saved my passphrase", + "passphraseFrameLabel": "Passphrase", + "title": "Your passphrase" + }, + "hasAccount": "Already have an account? <0>Login here.", + "login": { + "description": "Please enter your passphrase to login to your account", + "deviceLengthError": "Please enter a device name", + "passphraseLabel": "12-Word passphrase", + "passphrasePlaceholder": "Passphrase", + "submit": "Login", + "title": "Login to your account", + "validationError": "Incorrect or incomplete passphrase" + }, + "register": { + "information": { + "color1": "Profile color one", + "color2": "Profile color two", + "header": "Enter a name for your device and pick colours and a user icon of your choosing", + "icon": "User icon", + "next": "Next", + "title": "Account information" + } + }, + "trust": { + "failed": { + "text": "Did you configure it correctly?", + "title": "Failed to reach server" + }, + "host": "You are connecting to <0>{{hostname}} - please confirm you trust it before making an account", + "no": "Go back", + "title": "Do you trust this server?", + "yes": "I trust this server" + }, + "verify": { + "description": "Please enter your passphrase from earlier to confirm you have saved it and to create your account", + "invalidData": "Data is not valid", + "noMatch": "Passphrase doesn't match", + "passphraseLabel": "Your 12-word passphrase", + "recaptchaFailed": "ReCaptcha validation failed", + "register": "Create account", + "title": "Confirm your passphrase" + } + }, + "errors": { + "badge": "It broke", + "details": "Error details", + "reloadPage": "Reload the page", + "showError": "Show error details", + "title": "We encountered an error!" + }, + "footer": { + "legal": { + "disclaimer": "Disclaimer", + "disclaimerText": "sudo-flix does not host any files, it merely links to 3rd party services. Legal issues should be taken up with the file hosts and providers. sudo-flix is not responsible for any media files shown by the video providers." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Watch your favourite shows and movies with this open source streaming app." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "About", + "dmca": "DMCA", + "login": "Login", + "pagetitle": "{{title}} - sudo-flix", + "register": "Register", + "settings": "Settings", + "onboarding": "Setup" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Bookmarks" + }, + "continueWatching": { + "sectionTitle": "Continue Watching" + }, + "mediaList": { + "stopEditing": "Stop editing" + }, + "search": { + "allResults": "That's all we have!", + "failed": "Failed to find media, try again!", + "loading": "Loading...", + "noResults": "We couldn't find anything!", + "placeholder": "What do you want to watch?", + "sectionTitle": "Search results" + }, + "titles": { + "day": { + "default": "What would you like to watch this afternoon?", + "extra": [ + "Have you heard of South Beach Tow, its a trip." + ] + }, + "morning": { + "default": "What would you like to watch this morning?", + "extra": ["You should watch Worst Bakers in America."] + }, + "night": { + "default": "What would you like to watch tonight?", + "extra": ["Tired? Try winding down with Friends."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Movie", + "show": "Show" + } + }, + "navigation": { + "banner": { + "offline": "Check your internet connection" + }, + "menu": { + "about": "About us", + "donation": "Donate", + "logout": "Log out", + "register": "Sync to cloud", + "settings": "Settings", + "support": "Support" + } + }, + "notFound": { + "badge": "Not found", + "goHome": "Back to home", + "message": "We looked everywhere: under the bins, in the closet, behind the proxy but ultimately couldn't find the page you are looking for.", + "title": "Couldn't find that page" + }, + "overlays": { + "close": "Close" + }, + "player": { + "turnstile": { + "verifyingHumanity": "Verifying your humanity...", + "title": "We need to verify that you're human.", + "description": "Please verify that you are human by completing the Captcha on the right. This is to keep sudo-flix safe!", + "error": "Failed to verify your humanity. Please try again." + }, + "back": { + "default": "Back to home", + "short": "Back" + }, + "casting": { + "enabled": "Casting to device..." + }, + "menus": { + "settings": { + "videoSection": "Video settings", + "experienceSection": "Viewing experience", + "enableSubtitles": "Enable Subtitles", + "subtitleItem": "Subtitle settings", + "sourceItem": "Video sources", + "playbackItem": "Playback settings", + "downloadItem": "Download", + "qualityItem": "Quality" + }, + "episodes": { + "button": "Episodes", + "loadingTitle": "Loading...", + "loadingList": "Loading...", + "loadingError": "Error loading season", + "emptyState": "There are no episodes in this season, check back later!", + "episodeBadge": "E{{episode}}", + "unairedEpisodes": "One or more episodes in this season have been disabled because they haven't been aired yet." + }, + "sources": { + "title": "Sources", + "unknownOption": "Unknown", + "noStream": { + "title": "No stream", + "text": "This source has no streams for this movie or show." + }, + "noEmbeds": { + "title": "No embeds found", + "text": "We were unable to find any embeds, please try a different source." + }, + "failed": { + "title": "Failed to scrape", + "text": "There was an error while trying to find any videos, please try a different source." + } + }, + "subtitles": { + "title": "Subtitles", + "customizeLabel": "Customize", + "settings": { + "fixCapitals": "Fix capitalization", + "delay": "Subtitle delay", + "backlink": "Custom subtitles" + }, + "customChoice": "Select subtitle from file", + "offChoice": "Off", + "unknownLanguage": "Unknown" + }, + "downloads": { + "disclaimer": "Downloads are taken directly from the provider. sudo-flix does not have control over how the downloads are provided.", + "downloadSubtitle": "Download current subtitle", + "downloadPlaylist": "Download playlist", + "downloadVideo": "Download video", + "hlsDisclaimer": "Downloads are taken directly from the provider. sudo-flix does not have control over how the downloads are provided.

Please note that you are downloading an HLS playlist, it is not recommended to download if you are not familiar with advanced streaming formats. Try different sources for different formats.", + "onAndroid": { + "1": "To download on Android, click the download button then, on the new page, tap and hold on the video, then select save.", + "shortTitle": "Download / Android", + "title": "Downloading on Android" + }, + "onIos": { + "1": "To download on iOS, click the download button then, on the new page, click , then Save to Files .", + "shortTitle": "Download / iOS", + "title": "Downloading on iOS" + }, + "onPc": { + "1": "On PC, click the download button then, on the new page, right click the video and select Save video as", + "shortTitle": "Download / PC", + "title": "Downloading on PC" + }, + "title": "Download" + }, + "playback": { + "speedLabel": "Playback speed", + "title": "Playback settings" + }, + "quality": { + "automaticLabel": "Automatic quality", + "hint": "You can try <0>switching source 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 to get different quality options.", + "title": "Quality" + } + }, + "metadata": { + "failed": { + "badge": "Failed", + "homeButton": "Go home", + "text": "Could not load the media's metadata from TMDB. Please check whether TMDB is down or blocked on your internet connection.", + "title": "Failed to load metadata" + }, + "api": { + "text": "Could not load API metadata, please check your internet connection.", + "title": "Failed to load API metadata" + }, + "notFound": { + "badge": "Not found", + "homeButton": "Back to home", + "text": "We couldn't find the media you requested. Either it's been removed or you tampered with the URL.", + "title": "Couldn't find that media." + }, + "extensionPermission": { + "badge": "Permission Missing", + "title": "Configure the extension", + "text": "You have the browser extension, but we need your permission to get started using the extension.", + "button": "Use extension" + }, + "dmca": { + "badge": "Removed", + "title": "Media has been removed", + "text": "This media is no longer available due to a takedown notice or copyright claim." + } + }, + "nextEpisode": { + "cancel": "Cancel", + "next": "Next episode" + }, + "playbackError": { + "badge": "Playback error", + "errors": { + "errorAborted": "The fetching of the media was aborted by the user's request.", + "errorDecode": "Despite having previously been determined to be usable, an error occurred while trying to decode the media resource, resulting in an error.", + "errorGenericMedia": "Unknown media error occurred.", + "errorNetwork": "Some kind of network error occurred which prevented the media from being successfully fetched, despite having previously been available.", + "errorNotSupported": "The media or media provider object is not supported." + }, + "homeButton": "Go home", + "text": "There was an error trying to play the media. Please try again.", + "title": "Failed to play video!" + }, + "scraping": { + "items": { + "failure": "Error occurred", + "notFound": "Doesn't have the video", + "pending": "Checking for videos..." + }, + "notFound": { + "badge": "Not found", + "detailsButton": "Show details", + "homeButton": "Go home", + "text": "We have searched through our providers and cannot find the media you are looking for! We do not host the media and have no control over what is available. Please click 'Show details' below for more details.", + "title": "We couldn't find that" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} left • Finish at {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + } + }, + "screens": { + "dmca": { + "text": "Welcome to sudo-flix's DMCA contact page! We respect intellectual property rights and want to address any copyright concerns swiftly. If you believe your copyrighted work has been improperly used on our platform, please send a detailed DMCA notice to the email below. Please include a description of the copyrighted material, your contact details, and a statement of good faith belief. We're committed to resolving these matters promptly and appreciate your cooperation in keeping sudo-flix a place that respects creativity and copyrights.", + "title": "DMCA" + }, + "loadingApp": "Loading application", + "loadingUser": "Loading your profile", + "loadingUserError": { + "logout": "Logout", + "reset": "Reset custom server", + "text": "Failed to load your profile", + "textWithReset": "Failed to load your profile from your custom server, want to reset back to the default server?" + }, + "migration": { + "failed": "Failed to migrate your data.", + "inProgress": "Please hold, we are migrating your data. This shouldn't take long." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Device name", + "deviceNamePlaceholder": "Personal phone", + "editProfile": "Edit", + "logoutButton": "Log out" + }, + "actions": { + "delete": { + "button": "Delete account", + "confirmButton": "Delete account", + "confirmDescription": "Are you sure you want to delete your account? All your data will be lost!", + "confirmTitle": "Are you sure?", + "text": "This action is irreversible. All data will be deleted and nothing can be recovered.", + "title": "Delete account" + }, + "title": "Actions" + }, + "devices": { + "deviceNameLabel": "Device name", + "failed": "Failed to load sessions", + "removeDevice": "Remove", + "title": "Devices" + }, + "profile": { + "finish": "Finish editing", + "firstColor": "Profile color one", + "secondColor": "Profile color two", + "title": "Edit profile picture", + "userIcon": "User icon" + }, + "register": { + "cta": "Get started", + "text": "Share your watch progress between devices and keep them synced.", + "title": "Sync to the cloud" + }, + "title": "Account" + }, + "appearance": { + "activeTheme": "Active", + "themes": { + "blue": "Blue", + "default": "Default", + "gray": "Gray", + "red": "Red", + "teal": "Teal" + }, + "title": "Appearance" + }, + "subtitles": { + "title": "Subtitles", + "previewQuote": "I must not fear. Fear is the mind-killer.", + "textSizeLabel": "Text size", + "backgroundLabel": "Background opacity", + "colorLabel": "Color" + }, + "connections": { + "setup": { + "errorStatus": { + "title": "Something needs your attention", + "description": "It seems that one or more items in this setup need your attention." + }, + "unsetStatus": { + "title": "You haven't gone through setup", + "description": "Please click the button to the right to start the setup process." + }, + "successStatus": { + "title": "Everything is set up!", + "description": "All things are in place for you to start watching your favourite media." + }, + "redoSetup": "Redo setup", + "doSetup": "Do setup", + "itemError": "There is something wrong with this setting. Go through setup again to fix it.", + "items": { + "extension": "Extension", + "proxy": "Custom proxy", + "default": "Default setup" + } + }, + "server": { + "description": "If you would like to connect to a custom backend to store your data, enable this and provide the URL. <0>Instructions.", + "label": "Custom server", + "urlLabel": "Custom server URL" + }, + "title": "Connections", + "workers": { + "addButton": "Add new worker", + "description": "To make the application function, all traffic is routed through proxies. Enable this if you want to bring your own workers. <0>Instructions.", + "emptyState": "No workers yet, add one below", + "label": "Use custom proxy workers", + "urlLabel": "Worker URLs", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Application language", + "languageDescription": "Language applied to the entire application.", + "title": "Preferences", + "thumbnail": "Generate thumbnails", + "thumbnailDescription": "Most of the time, videos don't have thumbnails. You can enable this setting to generate them on the fly but they can make your video slower.", + "thumbnailLabel": "Generate thumbnails" + }, + "reset": "Reset", + "save": "Save", + "sidebar": { + "info": { + "appVersion": "App version", + "backendUrl": "Backend URL", + "backendVersion": "Backend version", + "hostname": "Hostname", + "insecure": "Insecure", + "notLoggedIn": "You are not logged in", + "secure": "Secure", + "title": "App information", + "unknownVersion": "Unknown", + "userId": "User ID" + } + }, + "unsaved": "You have unsaved changes" + }, + "onboarding": { + "start": { + "title": "Let's get you setup with sudo-flix", + "explainer": "To get the best streams possible. You will need to choose which streaming method you want to use.", + "options": { + "proxy": { + "quality": "Good quality", + "title": "Custom proxy", + "description": "Setup a proxy in just 5 minutes and gain access to great sources.", + "action": "Setup proxy" + }, + "extension": { + "quality": "Best quality", + "title": "Browser extension", + "description": "Install browser extension and gain access to the best sources.", + "action": "Install extension" + }, + "default": { + "text": "I don't want good quality streams,<0 /> <1>use the default setup" + } + } + }, + "proxy": { + "title": "Let's make a new proxy", + "explainer": "With the proxy method, you can get great quality streams by making a self-service proxy.", + "link": "Learn how to make a proxy", + "input": { + "label": "Proxy URL", + "placeholder": "https://", + "errorInvalidUrl": "Not a valid URL", + "errorConnection": "Could not connect to proxy", + "errorNotProxy": "Expected a proxy but got a website" + }, + "back": "Go back", + "submit": "Submit proxy" + }, + "extension": { + "title": "Let's start with an extension", + "explainer": "Using the browser extension, you can get the best streams we have to offer. With just a simple install.", + "explainerIos": "Unfortunately, the browser extension is not supported on IOS, Press Go back to choose another option.", + "extensionHelp": "If you've installed the extension but it's not detected. Open the extension through your browsers extension menu and follow the steps on screen.", + "notDetecting": "Installed on chrome but not showing up? Try reloading the page!", + "notDetectingAction": "Reload page", + "linkChrome": "Install Chrome extension", + "linkFirefox": "Install Firefox extension", + "back": "Go back", + "status": { + "loading": "Waiting for you to install the extension", + "disallowed": "Extension is not enabled for this page", + "disallowedAction": "Enable extension", + "failed": "Failed to request status", + "outdated": "Extension version too old", + "success": "Extension is working as expected!" + }, + "submit": "Continue" + }, + "defaultConfirm": { + "title": "Are you sure?", + "description": "The default setup does not have the best streams and can be unbearably slow.", + "cancel": "Cancel", + "confirm": "Use default setup" + } + } +} diff --git a/src/assets/locales/es.json b/src/assets/locales/es.json new file mode 100644 index 00000000..dfc0840f --- /dev/null +++ b/src/assets/locales/es.json @@ -0,0 +1,528 @@ +{ + "about": { + "description": "sudo-flix es una aplicación web que busca transmisiones en internet. El equipo tiene como objetivo un enfoque principalmente minimalista para consumir contenido.", + "faqTitle": "Preguntas frecuentes", + "q1": { + "body": "sudo-flix no aloja ningún contenido. Cuando haces clic en algo para ver, se busca en internet el medio seleccionado (En la pantalla de carga y en la pestaña 'fuentes de vídeo' puedes ver qué fuente estás utilizando). El contenido nunca se carga en sudo-flix, todo se realiza a través de este mecanismo de búsqueda.", + "title": "¿De dónde proviene el contenido?" + }, + "q2": { + "body": "No es posible solicitar una serie o película, sudo-flix no gestiona ningún contenido. Todo el contenido se ve a través de fuentes en internet.", + "title": "¿Dónde puedo solicitar una serie o película?" + }, + "q3": { + "body": "Nuestros resultados de búsqueda están alimentados por The Movie Database (TMDB) y se muestran independientemente de si nuestras fuentes realmente tienen el contenido.", + "title": "Los resultados de búsqueda muestran la serie o película, ¿por qué no puedo reproducirla?" + }, + "title": "Acerca de sudo-flix" + }, + "actions": { + "copied": "Copiado", + "copy": "Copiar" + }, + "auth": { + "createAccount": "¿Todavía no tienes una cuenta? <0>Crea una cuenta.", + "deviceNameLabel": "Nombre del dispositivo", + "deviceNamePlaceholder": "Teléfono personal", + "generate": { + "description": "Tu frase de contraseña actúa como tu nombre de usuario y contraseña. Asegúrate de mantenerlas seguras, ya que las necesitarás para iniciar sesión en tu cuenta", + "next": "He guardado mi frase de contraseña", + "passphraseFrameLabel": "Frase de contraseña", + "title": "Tu frase de contraseña" + }, + "hasAccount": "¿Ya tienes una cuenta? <0>Inicia sesión aquí.", + "login": { + "description": "Por favor, ingresa tu frase de contraseña para iniciar sesión en tu cuenta", + "deviceLengthError": "Por favor, ingresa un nombre de dispositivo", + "passphraseLabel": "Frase de contraseña de 12 palabras", + "passphrasePlaceholder": "Frase de contraseña", + "submit": "Iniciar sesión", + "title": "Inicia sesión en tu cuenta", + "validationError": "Contraseña incorrecta o incompleta" + }, + "register": { + "information": { + "color1": "Color de perfil uno", + "color2": "Color de perfil dos", + "header": "Ingresa un nombre para tu dispositivo, elige colores, y un ícono de usuario de tu elección", + "icon": "Ícono de usuario", + "next": "Siguiente", + "title": "Información de la cuenta" + } + }, + "trust": { + "failed": { + "text": "¿Lo configuraste correctamente?", + "title": "No se pudo conectar al servidor" + }, + "host": "Te estás conectando a <0>{{hostname}} - por favor, confirma si confías en este antes de crear una cuenta", + "no": "Regresar", + "title": "¿Confías en este servidor?", + "yes": "Confío en este servidor" + }, + "verify": { + "description": "Por favor, ingresa tu frase de contraseña anterior para confirmar que la has guardado y para crear tu cuenta", + "invalidData": "Los datos no son válidos", + "noMatch": "La frase de contraseña no coincide", + "passphraseLabel": "Tu frase de contraseña de 12 palabras", + "recaptchaFailed": "La validación de ReCaptcha falló", + "register": "Crear cuenta", + "title": "Confirma tu frase de contraseña" + } + }, + "errors": { + "badge": "Se descompuso", + "details": "Detalles del error", + "reloadPage": "Recargar la página", + "showError": "Mostrar detalles del error", + "title": "¡Encontramos un error!" + }, + "footer": { + "legal": { + "disclaimer": "Descargo de responsabilidad", + "disclaimerText": "sudo-flix no aloja ningún archivo, simplemente enlaza a servicios de terceros. Los problemas legales deben ser tratados con los proveedores de archivos y servicios. sudo-flix no se hace responsable de los archivos multimedia mostrados por los proveedores de vídeo." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Ve tus series y películas favoritas con esta aplicación de transmisión de código abierto." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Acerca de", + "dmca": "DMCA", + "login": "Iniciar sesión", + "onboarding": "Configurar", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registrarse", + "settings": "Configuración" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Marcadores" + }, + "continueWatching": { + "sectionTitle": "Continuar viendo" + }, + "mediaList": { + "stopEditing": "Detener edición" + }, + "search": { + "allResults": "¡Eso es todo lo que tenemos!", + "failed": "¡Error al encontrar contenido, inténtalo de nuevo!", + "loading": "Cargando...", + "noResults": "¡No pudimos encontrar nada!", + "placeholder": "¿Qué te gustaría ver?", + "sectionTitle": "Resultados de búsqueda" + }, + "titles": { + "day": { + "default": "¿Qué te gustaría ver esta tarde?", + "extra": [ + "¿Te sientes aventurero? Jurassic Park podría ser la elección perfecta." + ] + }, + "morning": { + "default": "¿Qué te gustaría ver esta mañana?", + "extra": ["Escuché que “Antes del amanecer” es buena"] + }, + "night": { + "default": "¿Qué te gustaría ver esta noche?", + "extra": ["¿Cansado? Escuché que “El Exorcista” es buena."] + } + } + }, + "media": { + "episodeDisplay": "T{{season}} E{{episode}}", + "types": { + "movie": "Película", + "show": "Serie" + } + }, + "navigation": { + "banner": { + "offline": "Verifica tu conexión a internet" + }, + "menu": { + "about": "Acerca de nosotros", + "donation": "Donar", + "logout": "Cerrar sesión", + "register": "Sincronizar con la nube", + "settings": "Configuración", + "support": "Soporte" + } + }, + "notFound": { + "badge": "No encontrado", + "goHome": "Volver a inicio", + "message": "Buscamos en todas partes: debajo de los contenedores, en el armario, detrás del proxy, pero al final no pudimos encontrar la página que estás buscando.", + "title": "No se puede encontrar esa página" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Cancelar", + "confirm": "Usar configuración por defecto", + "description": "La configuración predeterminada no tiene las mejores transmisiones y puede ser insoportablemente lenta.", + "title": "Estás seguro?" + }, + "extension": { + "back": "Volver atrás", + "explainer": "Usando la extensión de navegador, puedes obtener las mejores transiciones que ofrecemos. Simplemente con una simple instalación.", + "extensionHelp": "Si has instalado la extensión pero no es detectada, abre la extensión desde la sección en el menú de extensiones y sigue los pasos de la pantalla.", + "status": { + "disallowed": "La extensión no está activada para esta página", + "disallowedAction": "Activar extensión", + "failed": "No se pudo solicitar el estado", + "loading": "Esperando que instales la extensión", + "outdated": "La versión de la extensión es muy antigua", + "success": "¡La extensión está funcionando como se esperaba!" + }, + "submit": "Continuar", + "title": "Vamos a empezar con la extensión" + }, + "proxy": { + "back": "Volver atrás", + "explainer": "Con el método de proxy, puede obtener una my buena calidad de las transmisiones creando un proxy propio.", + "input": { + "errorConnection": "No se pudo conectar al proxy", + "errorInvalidUrl": "URL no válida", + "errorNotProxy": "Esperaba un proxy pero obtuve un sitio web", + "label": "URL del Proxy", + "placeholder": "https://" + }, + "link": "Aprender a crear un proxy", + "submit": "Enviar proxy", + "title": "Vamos a crear un nuevo proxy" + }, + "start": { + "explainer": "Para conseguir las mejores transmisiones, necesitarás elegir que método de transmisión quieres usar.", + "options": { + "default": { + "text": "No quiero buena calidad en las transmisiones,<0 /> <1>usar la configuración por defecto" + }, + "extension": { + "action": "Instalar extensión", + "description": "Instala la extensión de navegador y gana acceso a las mejore fuentes.", + "quality": "La mejor calidad", + "title": "Extensión de navegador" + }, + "proxy": { + "action": "Configurar proxy", + "description": "Configura un proxy en tan solo 5 minutos y obtén acceso a buenas fuentes.", + "quality": "Buena calidad", + "title": "Proxy personalizado" + } + }, + "title": "Vamos a configurar sudo-flix" + } + }, + "overlays": { + "close": "Cerrar" + }, + "player": { + "back": { + "default": "Volver al inicio", + "short": "Volver" + }, + "casting": { + "enabled": "Transmitiendo a dispositivo..." + }, + "menus": { + "downloads": { + "disclaimer": "Las descargas se realizan directamente desde el proveedor. sudo-flix no tiene control sobre cómo se proporcionan las descargas.", + "downloadPlaylist": "Descargar lista de reproducción", + "downloadSubtitle": "Descargar subtítulo actual", + "downloadVideo": "Descargar vídeo", + "hlsDisclaimer": "Las descargas se realizan directamente del proveedor. sudo-flix no tiene control sobre cómo se proporcionan las descargas.

Tenga en cuenta que está descargando una lista de reproducción HLS, dirigida a usuarios familiarizados con la transmisión multimedia avanzada.", + "onAndroid": { + "1": "Para descargar en Android, haz clic en el botón de descarga y luego, en la nueva página, mantén presionado el vídeo y selecciona guardar.", + "shortTitle": "Descargar / Android", + "title": "Descargando en Android" + }, + "onIos": { + "1": "Para descargar en iOS, haz clic en el botón de descarga y luego, en la nueva página, haz clic en , luego Guardar en archivos .", + "shortTitle": "Descargar / iOS", + "title": "Descargando en iOS" + }, + "onPc": { + "1": "En una PC, haz clic en el botón de descarga y luego, en la nueva página, haz clic derecho en el vídeo y selecciona Guardar vídeo como...", + "shortTitle": "Descargar / PC", + "title": "Descargando en PC" + }, + "title": "Descargar" + }, + "episodes": { + "button": "Episodios", + "emptyState": "No hay episodios en esta temporada, ¡Intenta más tarde!", + "episodeBadge": "E{{episode}}", + "loadingError": "Error al cargar la temporada", + "loadingList": "Cargando...", + "loadingTitle": "Cargando...", + "unairedEpisodes": "Uno o más episodios de esta temporada se han desactivado porque aún no se han emitido." + }, + "playback": { + "speedLabel": "Velocidad de reproducción", + "title": "Configuración de reproducción" + }, + "quality": { + "automaticLabel": "Calidad automática", + "hint": "Puedes intentar <0>cambiar de fuente para obtener diferentes opciones de calidad.", + "iosNoQuality": "Debido a limitaciones definidas por Apple, la selección de calidad no está disponible en iOS para esta fuente. Puedes intentar <0>cambiar a otra fuente para obtener diferentes opciones de calidad.", + "title": "Calidad" + }, + "settings": { + "downloadItem": "Descargar", + "enableSubtitles": "Habilitar subtítulos", + "experienceSection": "Experiencia de visualización", + "playbackItem": "Configuración de reproducción", + "qualityItem": "Calidad", + "sourceItem": "Fuentes de vídeo", + "subtitleItem": "Configuración de subtítulos", + "videoSection": "Configuración de vídeo" + }, + "sources": { + "failed": { + "text": "Se produjo un error al intentar encontrar vídeos, por favor intenta con una fuente diferente.", + "title": "Error al obtener" + }, + "noEmbeds": { + "text": "No pudimos encontrar ninguna embed, por favor intenta con una fuente diferente.", + "title": "No se encontraron embeds" + }, + "noStream": { + "text": "Esta fuente no tiene transmisiones para esta película o serie.", + "title": "Sin transmisión" + }, + "title": "Fuentes", + "unknownOption": "Desconocida" + }, + "subtitles": { + "customChoice": "Seleccionar subtítulo del archivo", + "customizeLabel": "Personalizar", + "offChoice": "Apagado", + "settings": { + "backlink": "Subtítulos personalizados", + "delay": "Retraso de subtítulos", + "fixCapitals": "Arreglar capitalización" + }, + "title": "Subtítulos", + "unknownLanguage": "Desconocido" + } + }, + "metadata": { + "api": { + "text": "No ha sido posible cargar la metadata de la API, por favor, comprueba tu conexión a internet.", + "title": "No ha sido posible cargar los metadatos de la API" + }, + "dmca": { + "badge": "Eliminado", + "text": "Este contenido no va a estar disponible debido a un aviso de eliminación o un reclamo de derechos de autor.", + "title": "El contenido ha sido eliminado" + }, + "extensionPermission": { + "badge": "Permisos insuficientes", + "button": "Usar extensión", + "text": "Tienes la extensión de navegador, pero necesitamos tu permiso para empezar a usarla.", + "title": "Configura la extensión" + }, + "failed": { + "badge": "Error", + "homeButton": "Ir al inicio", + "text": "No se pudieron cargar los metadatos del contenido de TMDB. Por favor, verifica si TMDB está caído o bloqueado en tu conexión a internet.", + "title": "Error al cargar los metadatos" + }, + "notFound": { + "badge": "No encontrado", + "homeButton": "Volver al inicio", + "text": "No pudimos encontrar el contenido que solicitaste. Ya sea que se haya eliminado o se haya modificado la URL.", + "title": "No se pudo encontrar ese contenido." + } + }, + "nextEpisode": { + "cancel": "Cancelar", + "next": "Siguiente episodio" + }, + "playbackError": { + "badge": "Error de reproducción", + "errors": { + "errorAborted": "La obtención del contenido fue cancelada por solicitud del usuario.", + "errorDecode": "A pesar de haber sido determinado previamente como utilizable, se produjo un error al intentar decodificar el recurso del contenido, lo que resultó en un error.", + "errorGenericMedia": "Se produjo un error desconocido en el contenido.", + "errorNetwork": "Se produjo un error de red que impidió obtener el contenido de manera exitosa, a pesar de haber estado disponible anteriormente.", + "errorNotSupported": "El contenido o el proveedor del contenido no es compatible." + }, + "homeButton": "Ir al inicio", + "text": "Se produjo un error al intentar reproducir el contenido. Por favor, inténtalo de nuevo.", + "title": "¡No se pudo reproducir el vídeo!" + }, + "scraping": { + "items": { + "failure": "Ocurrió un error", + "notFound": "No tiene el vídeo", + "pending": "Verificando vídeos..." + }, + "notFound": { + "badge": "No encontrado", + "detailsButton": "Mostrar detalles", + "homeButton": "Ir al inicio", + "text": "Hemos buscado en nuestros proveedores y no podemos encontrar el contenido que estás buscando. No alojamos el contenido y no tenemos control sobre lo que está disponible. Haz clic en 'Mostrar detalles' a continuación para obtener más información.", + "title": "No pudimos encontrar eso" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} restante • Finaliza a las {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Por favor, confirma que eres humano completando el Captcha. Esto es para mantener sudo-flix seguro!", + "error": "Ha habido un error al verificar tu humanidad. Por favor, prueba de nuevo.", + "title": "Necesitamos verificar que eres humano.", + "verifyingHumanity": "Verificando tu hunanidad…" + } + }, + "screens": { + "dmca": { + "text": "¡Bienvenido a la página de contacto DMCA de sudo-flix! Respetamos los derechos de propiedad intelectual y queremos abordar cualquier problema de derechos de autor de manera rápida. Si cree que su trabajo con derechos de autor se ha utilizado incorrectamente en nuestra plataforma, envíe un aviso DMCA detallado al correo electrónico que se muestra a continuación. Incluya una descripción del material con derechos de autor, sus datos de contacto y una declaración de buena fe. Estamos comprometidos a resolver estos asuntos de manera pronta y agradecemos su cooperación para mantener a sudo-flix como un lugar que respeta la creatividad y los derechos de autor.", + "title": "DMCA" + }, + "loadingApp": "Cargando la aplicación", + "loadingUser": "Cargando tu perfil", + "loadingUserError": { + "logout": "Cerrar sesión", + "reset": "Restablecer servidor personalizado", + "text": "Error al cargar tu perfil", + "textWithReset": "Error al cargar tu perfil desde tu servidor personalizado, ¿Quieres restablecerlo al servidor predeterminado?" + }, + "migration": { + "failed": "Error al migrar tus datos.", + "inProgress": "Por favor espera, estamos migrando tus datos. Esto no debería tomar mucho tiempo." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nombre del dispositivo", + "deviceNamePlaceholder": "Teléfono personal", + "editProfile": "Editar", + "logoutButton": "Cerrar sesión" + }, + "actions": { + "delete": { + "button": "Eliminar cuenta", + "confirmButton": "Eliminar cuenta", + "confirmDescription": "¿Estás seguro de que quieres eliminar tu cuenta? ¡Todos tus datos se perderán!", + "confirmTitle": "¿Estás seguro?", + "text": "Esta acción es irreversible. Todos los datos se eliminarán y no se podrán recuperar.", + "title": "Eliminar cuenta" + }, + "title": "Acciones" + }, + "devices": { + "deviceNameLabel": "Nombre del dispositivo", + "failed": "Error al cargar las sesiones", + "removeDevice": "Eliminar", + "title": "Dispositivos" + }, + "profile": { + "finish": "Finalizar edición", + "firstColor": "Color de perfil uno", + "secondColor": "Color de perfil dos", + "title": "Editar imagen de perfil", + "userIcon": "Icono de usuario" + }, + "register": { + "cta": "Empezar", + "text": "Comparte tu progreso de visualización entre dispositivos y manténlos sincronizados.", + "title": "Sincronizar con la nube" + }, + "title": "Cuenta" + }, + "appearance": { + "activeTheme": "Activo", + "themes": { + "blue": "Azul", + "default": "Predeterminado", + "gray": "Gris", + "red": "Rojo", + "teal": "Turquesa" + }, + "title": "Apariencia" + }, + "connections": { + "server": { + "description": "Si deseas conectarte a un backend personalizado para almacenar tus datos, habilita esto y proporciona la URL. <0>Instrucciones.", + "label": "Servidor personalizado", + "urlLabel": "URL del servidor personalizado" + }, + "setup": { + "doSetup": "Configurar", + "errorStatus": { + "description": "Parece que hay uno o más items en esta configuración que requieren de tu atención.", + "title": "Algo necesita de tu atención" + }, + "itemError": "Hay algo que no fue bien con esta configuración. Ve otra vez a la configuración para solucionarlo.", + "items": { + "default": "Configuración por defecto", + "extension": "Extensión", + "proxy": "Proxy personalizado" + }, + "redoSetup": "Rehacer configuración", + "successStatus": { + "description": "Todo lo necesario está en su sitio para que empieces a ver tu contenido favorito.", + "title": "Todo está configurado!" + }, + "unsetStatus": { + "description": "Haga clic en el botón a la derecha para iniciar el proceso de configuración.", + "title": "No has pasado por la configuración" + } + }, + "title": "Conexiones", + "workers": { + "addButton": "Agregar nuevo worker", + "description": "Para que la aplicación funcione, todo el tráfico se enruta a través de proxies. Habilita esto si quieres usar tus propios workers. <0>Instrucciones.", + "emptyState": "Aún no hay workers, agrega uno a continuación", + "label": "Usar proxy workers personalizados", + "urlLabel": "URL de los workers", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Idioma de la aplicación", + "languageDescription": "Idioma aplicado a toda la aplicación.", + "thumbnail": "Generar miniaturas", + "thumbnailDescription": "La mayor parte del tiempo, los videos no tienen miniaturas. Puedes activar esta configuración para generarlas sobre la marcha, pero pueden realentizar tu video.", + "thumbnailLabel": "Generar miniaturas", + "title": "Preferencias" + }, + "reset": "Restablecer", + "save": "Guardar", + "sidebar": { + "info": { + "appVersion": "Versión de la aplicación", + "backendUrl": "URL del backend", + "backendVersion": "Versión del backend", + "hostname": "Nombre de host", + "insecure": "No seguro", + "notLoggedIn": "No has iniciado sesión", + "secure": "Seguro", + "title": "Información de la aplicación", + "unknownVersion": "Desconocida", + "userId": "ID de usuario" + } + }, + "subtitles": { + "backgroundLabel": "Opacidad del fondo", + "colorLabel": "Color", + "previewQuote": "No debo temer. El miedo es el asesino de la mente.", + "textSizeLabel": "Tamaño del texto", + "title": "Subtítulos" + }, + "unsaved": "Tienes cambios sin guardar" + } +} diff --git a/src/assets/locales/et.json b/src/assets/locales/et.json new file mode 100644 index 00000000..25dc25d3 --- /dev/null +++ b/src/assets/locales/et.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "sudo-flix on veebirakendus, mis otsib internetti voogude jaoks. Meeskonna eesmärk on enamasti minimalistlik lähenemine sisu tarbimisele.", + "faqTitle": "Tihti küsitud küsimused", + "q1": { + "body": "sudo-flix ei hoiusta enda sisu. Kui klõpsate, et midagi vaadata, otsitakse valitud meediat internetist (laadimisekraanil ja vahekaardil \"videoallikad\" näete, millist allikat te kasutate). Meediat ei lae sudo-flix kunagi üles, kõik toimub selle otsingumehhanismi kaudu.", + "title": "Kust tuleb meie sisu?" + }, + "q2": { + "body": "Saadet või filmi ei ole võimalik taotelda, sudo-flix ei halda mingit sisu. Kogu sisu vaadatakse interneti allikate kaudu.", + "title": "Kust ma saan kindlat saadet või filmi paluda?" + }, + "q3": { + "body": "Meie otsingutulemusi toetab The Movie Database (TMDB) ja need kuvatakse sõltumata sellest, kas meie allikates on sisu tegelikult olemas.", + "title": "Otsingutulemustes kuvatakse saade või film. Miks ma ei saa seda esitada?" + }, + "title": "sudo-flix'i kohta" + }, + "actions": { + "copied": "Kopeeritud", + "copy": "Kopeeri" + }, + "auth": { + "createAccount": "Ei ole kontot veel?<0>Loo konto.", + "deviceNameLabel": "Seadme nimi", + "deviceNamePlaceholder": "Isiklik telefon", + "generate": { + "description": "Teie paroolifraas toimib teie kasutajanime ja paroolina. Veenduge, et see oleks ohutus kohas, kuna peate kasutama seda, et logida oma kontosse sisse", + "next": "Olen salvestanud oma paroolifraasi", + "passphraseFrameLabel": "Paroolifraas", + "title": "Sinu paroolifraas" + }, + "hasAccount": "On juba konto? <0>Logi sisse.", + "login": { + "description": "Palun sisestage paroolifraas, et logida kontosse sisse", + "deviceLengthError": "Palun sisesta seadme nimi", + "passphraseLabel": "12-Sõnaline paroolifraas", + "passphrasePlaceholder": "Paroolifraas", + "submit": "Logi sisse", + "title": "Logi sisse oma kontosse", + "validationError": "Vale vōi mittetäielik paroolifraas" + }, + "register": { + "information": { + "color1": "Profiili värv üks", + "color2": "Profiili värv kaks", + "header": "Sisestage oma seadme nimi ja valige värvid ning kasutajaikoon", + "icon": "Kasutaja ikoon", + "next": "Järgmine", + "title": "Kontoteave" + } + }, + "trust": { + "failed": { + "text": "Kas sa seadistasid selle õigesti?", + "title": "Serveriga ühenduse loomine ebaõnnestus" + }, + "host": "Ühendate <0>{{hostname}} - enne konto tegemist kinnitage, et usaldate seda", + "no": "Tagasi", + "title": "Kas usaldate seda serverit?", + "yes": "Usaldan seda serverit" + }, + "verify": { + "description": "Palun sisestage oma paroolifraas, et kinnitada, et olete selle salvestanud ja luua oma konto", + "invalidData": "Andmed ei kehti", + "noMatch": "Paroolifraas ei klapi", + "passphraseLabel": "Sinu 12-sõnaline paroolifraas", + "recaptchaFailed": "ReCaptcha valideerimine ebaõnnestua", + "register": "Loo konto", + "title": "Kinnita oma paroolifraas" + } + }, + "errors": { + "badge": "Läks katki", + "details": "Vea üksikasjad", + "reloadPage": "Värskenda leht", + "showError": "Näita vea üksikasju", + "title": "Tekkis viga!" + }, + "footer": { + "legal": { + "disclaimer": "Lahtiütlemine", + "disclaimerText": "sudo-flix ei hoiusta ühtegi faili, vaid ainult lingib kolmandate osapoolte teenustele. Juriidilised küsimused tuleks lahendada failihostide ja teenusepakkujatega. sudo-flix ei vastuta videopakkujate poolt näidatud meediafailide eest." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Vaadake selle avatud lähtekoodiga voogesituse rakendusega oma lemmiksaateid ja -filme." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Meist", + "dmca": "DMCA", + "login": "Logi sisse", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registreeri", + "settings": "Sätted" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Järjehoidjad" + }, + "continueWatching": { + "sectionTitle": "Jätka vaatamist" + }, + "mediaList": { + "stopEditing": "Lõpeta redigeerimine" + }, + "search": { + "allResults": "See on kõik, mis meil on!", + "failed": "Meedia leidmine ebaõnnestus, proovige uuesti!", + "loading": "Laadimine....", + "noResults": "Me ei leidnud midagi!", + "placeholder": "Mida tahate vaadata?", + "sectionTitle": "Otsingutulemused" + }, + "titles": { + "day": { + "default": "Mida te soovite täna pärastlõunal vaadata?", + "extra": [ + "Kas tunnete end seiklushimulisena? Jurassic Park võib olla ideaalne valik." + ] + }, + "morning": { + "default": "Mida te soovite täna hommikul vaadata?", + "extra": ["Ma kuulsin, et Before Sunrise on hea"] + }, + "night": { + "default": "Mida te soovite täna õhtul vaadata?", + "extra": ["Väsinud? Olen kuulnud, et The Exorcist on hea."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Saade" + } + }, + "navigation": { + "banner": { + "offline": "Kontrollige oma Interneti-ühendust" + }, + "menu": { + "about": "Meie kohta", + "donation": "Anneta", + "logout": "Logi välja", + "register": "Sünkrooni pilvega", + "settings": "Sätted", + "support": "Abi" + } + }, + "notFound": { + "badge": "Ei leitud", + "goHome": "Tagasi koju", + "message": "Otsisime kõikjalt: prügikastide alt, kapist, proksi tagant, kuid lõpuks ei leidnud otsitud lehte.", + "title": "Ei leidnud seda lehekülge" + }, + "overlays": { + "close": "Sulge" + }, + "player": { + "back": { + "default": "Tagasi koju", + "short": "Tagasi" + }, + "casting": { + "enabled": "Seadmesse ülekandmine..." + }, + "menus": { + "downloads": { + "disclaimer": "Allalaadimine toimub otse teenusepakkujalt. sudo-flix ei saa kontrollida, kuidas allalaadimine toimub.", + "downloadPlaylist": "Laadige esitusloend alla", + "downloadSubtitle": "Laadige alla praegune subtiiter", + "downloadVideo": "Lae alla video", + "hlsDisclaimer": "Allalaadimised võetakse otse teenusepakkujalt. sudo-flix ei kontrolli, kuidas allalaadimisi pakutakse. Pange tähele, et laadite alla HLS-i esitusloendit, see on mõeldud kasutajatele, kes on tuttavad multimeediumi voogesitusega.", + "onAndroid": { + "1": "Et allalaadida Androidi telefonil, vajuta allalaadimise nupule, siis uuel leheküljel vajuta ja hoia video peal, siis vali salvesta.", + "shortTitle": "Allalaadimine / Android", + "title": "Androidil allalaadimine" + }, + "onIos": { + "1": "Et allalaadida iOS telefonil, vajuta allalaadimise nupule, siis uuel leheküljel vajuta , siis Salvesta failidesse .", + "shortTitle": "Allalaadimine / iOS", + "title": "iOSil allalaadimine" + }, + "onPc": { + "1": "Klõpsake arvutis allalaadimise nupule, seejärel klõpsake uuel lehel parema klõpsuga videol ja valige Salvesta video", + "shortTitle": "Allalaadimine / Arvutis", + "title": "Arvutis allalaadimine" + }, + "title": "Lae alla" + }, + "episodes": { + "button": "Episoodid", + "emptyState": "Selles hooajas puuduvad episoodid, vaadake hiljem uuesti!", + "episodeBadge": "E{{episode}}", + "loadingError": "Hooaja laadimine ebaōnnestus", + "loadingList": "Laadimine...", + "loadingTitle": "Laadimine...", + "unairedEpisodes": "Üks või mitu selle hooaja episoodi on välja lülitatud, sest neid ei ole veel eetris olnud." + }, + "playback": { + "speedLabel": "Taasesituse kiirus", + "title": "Taasesituse sätted" + }, + "quality": { + "automaticLabel": "Automaatne kvaliteet", + "hint": "Võid proovida <0>vahetada allikat, et saada teisi kvaliteedi valikuid.", + "iosNoQuality": "Apple'i poolt määratletud piirangute tõttu ei ole sellel allikal kvaliteedivalik saadaval. Te võite proovida <0>vahetada teisele allikale, et saada erinevaid kvaliteedivalikuid.", + "title": "Kvaliteet" + }, + "settings": { + "downloadItem": "Lae alla", + "enableSubtitles": "Luba Subtiitrid", + "experienceSection": "Vaatamise kogemus", + "playbackItem": "Taasesituse sätted", + "qualityItem": "Kvaliteet", + "sourceItem": "Video allikad", + "subtitleItem": "Subtiitri sätted", + "videoSection": "Video sätted" + }, + "sources": { + "failed": { + "text": "Videote leidmisel tekkis viga, palun proovige mõnda muud allikat.", + "title": "Ei õnnestunud kraapida" + }, + "noEmbeds": { + "text": "Me ei leidnud ühtegi manust, proovige twist allikat.", + "title": "Ei leitud manuseid" + }, + "noStream": { + "text": "Sellel allikal ei ole selle filmi või seriaali jaoks ühtegi voogedastust.", + "title": "Puudub voog" + }, + "title": "Allikad", + "unknownOption": "Teadmata" + }, + "subtitles": { + "customChoice": "Vali subtiitrid failist", + "customizeLabel": "Kohanda", + "offChoice": "Välja", + "settings": { + "backlink": "Kohandatud subtiitrid", + "delay": "Subtiitrite viivitus", + "fixCapitals": "Parandage suurtähtede kasutamine" + }, + "title": "Subtiitrid", + "unknownLanguage": "Tundmatu" + } + }, + "metadata": { + "api": { + "text": "API metaandmete laadimine ebaõnnestus, palun kontrollige oma internetiühendust.", + "title": "API metaandmete laadimine ebaõnnestus" + }, + "failed": { + "badge": "Ebaōnnestus", + "homeButton": "Mine koju", + "text": "Meedia metaandmeid ei õnnestunud TMDB-st laadida. Palun kontrollige, kas TMDB on maas või see on teie internetiühenduses blokeeritud.", + "title": "Metaandmete laadimine ebaõnnestus" + }, + "notFound": { + "badge": "Ei leitud", + "homeButton": "Tagasi koju", + "text": "Me ei leidnud teie soovitud meediat. Kas see on eemaldatud või olete URL-i muutnud.", + "title": "Ei leidnud seda meediat." + } + }, + "nextEpisode": { + "cancel": "Tühista", + "next": "Järgmine episood" + }, + "playbackError": { + "badge": "Taasesituse viga", + "errors": { + "errorAborted": "Meedia kättesaamine katkestati kasutaja taotlusel.", + "errorDecode": "Vaatamata sellele, et eelnevalt oli kindlaks tehtud, et see on kasutatav, tekkis viga meediaressursi dekodeerimisel, mille tulemuseks oli viga.", + "errorGenericMedia": "Tekkis tundmatu meediaviga.", + "errorNetwork": "Tekkis võrguviga, mis takistas meedia edukat kättesaamist, kuigi see oli varem saadaval.", + "errorNotSupported": "Meedia või meediapakkuja objekt ei ole toetatud." + }, + "homeButton": "Mine koju", + "text": "Tekkis viga meedia mängimisel. Palun proovige uuesti.", + "title": "Video mängimine ebaōnnestus!" + }, + "scraping": { + "items": { + "failure": "Tekkis viga", + "notFound": "Ei ole videot", + "pending": "Otsin videoid..." + }, + "notFound": { + "badge": "Ei leitud", + "detailsButton": "Näita üksikasju", + "homeButton": "Mine koju", + "text": "Me otsisime läbi meie teenusepakkujad ja ei leidnud teie poolt otsitud meediat! Me ei halda meediat ja meil ei ole kontrolli selle üle, mis on saadaval. Täpsema teabe saamiseks klõpsake allpool nupule \"Näita üksikasju\".", + "title": "Me ei leidnud seda" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} alles • Lõppeb {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Palun kinnitage, et olete inimene, täites paremal asuva Captcha. See on selleks, et hoida sudo-flix turvalisena!", + "error": "Ei õnnestunud kontrollida teie inimlikkust. Palun proovige uuesti.", + "title": "Me peame kontrollima, et te olete inimene.", + "verifyingHumanity": "Kontrollime kas olete robot..." + } + }, + "screens": { + "dmca": { + "text": "Tere tulemast sudo-flixi DMCA kontaktilehele! Me austame intellektuaalomandi õigusi ja soovime kiiresti lahendada kõik autoriõigusega seotud probleemid. Kui usute, et teie autoriõigusega kaitstud tööd on meie platvormil vääralt kasutatud, saatke palun üksikasjalik DMCA-teade alljärgnevale e-posti aadressile. Palun lisage autoriõigusega kaitstud materjali kirjeldus, oma kontaktandmed ja avaldus heauskse veendumuse kohta. Oleme pühendunud nende küsimuste kiirele lahendamisele ja hindame teie koostööd, et sudo-flix oleks koht, kus austatakse loovust ja autoriõigusi.", + "title": "DMCA" + }, + "loadingApp": "Rakenduse laadimine", + "loadingUser": "Teie profiili laadimine", + "loadingUserError": { + "logout": "Logi välja", + "reset": "Lähtestage kohandatud server", + "text": "Profiili laadimine ebaōnnestus", + "textWithReset": "Profiili laadimine kohandatud serverist ebaõnnestus. Kas soovite lähtestada vaikeserverile?" + }, + "migration": { + "failed": "Teie andmete üleviimine ebaõnnestus.", + "inProgress": "Palun oodake, me viime teie andmeid üle. See ei tohiks kaua aega võtta." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Seadme nimi", + "deviceNamePlaceholder": "Isiklik telefon", + "editProfile": "Muuda", + "logoutButton": "Logi välja" + }, + "actions": { + "delete": { + "button": "Kustuta konto", + "confirmButton": "Kustuta konto", + "confirmDescription": "Kas olete kindel, et soovite oma konto kustutada? Kõik teie andmed lähevad kaotsi!", + "confirmTitle": "Oled sa kindel?", + "text": "See toiming on pöördumatu. Kõik andmed kustutatakse ja midagi ei saa taastada.", + "title": "Kustuta konto" + }, + "title": "Tegevused" + }, + "devices": { + "deviceNameLabel": "Seadme nimi", + "failed": "Seansside laadimine ebaõnnestus", + "removeDevice": "Eemalda", + "title": "Seadmed" + }, + "profile": { + "finish": "Lõpeta redigeerimine", + "firstColor": "Profiili värv üks", + "secondColor": "Profiili värv kaks", + "title": "Redigeeri profiilipilti", + "userIcon": "Kasutaja ikoon" + }, + "register": { + "cta": "Alustama", + "text": "Jagage oma vaatamise edenemist seadmete vahel ja hoidke neid sünkroonituna.", + "title": "Sünkrooni pilvega" + }, + "title": "Konto" + }, + "appearance": { + "activeTheme": "Aktiivne", + "themes": { + "blue": "Sinine", + "default": "Vaikimisi", + "gray": "Hall", + "red": "Punane", + "teal": "Tumesinine" + }, + "title": "Välimus" + }, + "connections": { + "server": { + "description": "Kui soovite kohandatud backend'i kasutada oma andmete hoidmiseks, siis lubage see ja varustage URL.", + "label": "Kohandatud server", + "urlLabel": "Kohandatud serveri URL" + }, + "title": "Ühendused", + "workers": { + "addButton": "Lisa uus töötaja", + "description": "Rakenduse toimimiseks suunatakse kogu liiklus puhverserverite kaudu. Lubage see, kui soovite oma puhverserverit kasutada.", + "emptyState": "Töötajaid pole veel, lisage üks", + "label": "Kasutage kohandatud puhverserveri töötajaid", + "urlLabel": "Töötajate URL-id", + "urlPlaceholder": "https://" + } + }, + "reset": "Lähtesta", + "save": "Salvesta", + "sidebar": { + "info": { + "appVersion": "Rakenduse versioon", + "backendUrl": "Backend'i URL", + "backendVersion": "Backend'i versioon", + "hostname": "Hostinimi", + "insecure": "Ebakindel", + "notLoggedIn": "Sa pole sisse loginud", + "secure": "Turvaline", + "title": "Rakenduse teave", + "unknownVersion": "Teadmatu", + "userId": "Kasutaja ID" + } + }, + "subtitles": { + "backgroundLabel": "Tausta läbipaistmatus", + "colorLabel": "Värv", + "previewQuote": "Ma ei tohi karta. Hirm on meelemõrvar.", + "textSizeLabel": "Teksti suurus", + "title": "Subtiitrid" + }, + "unsaved": "Teil on salvestamata muudatusi" + } +} diff --git a/src/assets/locales/fa.json b/src/assets/locales/fa.json new file mode 100644 index 00000000..09d837d2 --- /dev/null +++ b/src/assets/locales/fa.json @@ -0,0 +1,530 @@ +{ + "about": { + "description": "مووی-وب یک برنامه تحت وب است که در اینترنت به دنبال استریم ها می‌گردد. هدف تیم تجربه‌ای ساده برای استفاده از محتوا است.", + "faqTitle": "سوالات متداول", + "q1": { + "body": "مووی-وب هیچ محتوایی را میزبانی نمی‌کند. وقتی برای تماشا روی یک چیز کلیک می‌کنید، ما در اینترنت به دنبال محتوای مورد نظر شما می‌گردیم (در صفحه دانلود و در تب 'منابع ویدیو' می‌توانید ببینید که از کدام منبع استفاده می‌کنید). محتوا هرگز در مووی-وب آپلود نمی‌شود، همه چیز از طریق این مکانیزم جستجو اتفاق می‌افتد.", + "title": "فیلم ها از کجا می‌آیند؟" + }, + "q2": { + "body": "از آنجایی که محتوا ها توسط مووی-وب مدیریت نمی‌شوند، امکان درخواست فیلم وجود ندارد. تمام محتوا از طریق منابع در اینترنت به شما نشان داده می‌شوند.", + "title": "از کجا می‌توانم درخواست فیلم کنم؟" + }, + "q3": { + "body": "نتایج جستجوی ما توسط پایگاه داده فیلم (TMDB) تامین می‌شوند و نمایش داده می‌شوند، بدون اینکه مهم باشد که منابع ما واقعاً محتوا را داشته باشند یا خیر.", + "title": "نتایج جستجو وجود دارد، اما چرا فیلم پخش نمی‌شود؟" + }, + "title": "درباره مووی-وب" + }, + "actions": { + "copied": "کپی شد", + "copy": "کپی" + }, + "auth": { + "createAccount": "هنوز حساب کاربری ندارید؟ <0>ثبت نام کنید.", + "deviceNameLabel": "نام دستگاه", + "deviceNamePlaceholder": "تلفن شخصی", + "generate": { + "description": "جمله امنیتی شما به عنوان نام کاربری و رمز عبور عمل می‌کند. آن را در جایی ذخیره کنید چون برای ورود به آن نیاز دارید", + "next": "جمله امنیتی خود را ذخیره کرده‌ام", + "passphraseFrameLabel": "جمله امنیتی", + "title": "جمله امنیتی شما" + }, + "hasAccount": "حساب کاربری دارید؟ <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}} هستید - لطفا قبل از ایجاد حساب کاربری خود از اعتماد به آن اطمینان حاصل کنید", + "no": "بازگشت", + "title": "آیا به این سرور اعتماد دارید؟", + "yes": "بله اعتماد دارم" + }, + "verify": { + "description": "لطفا جمله امنیتی که به شما داده شده را مجدد وارد تا کنید تا حساب کاربری ساخته شود", + "invalidData": "اطلاعات معتبر نیست", + "noMatch": "جمله امنیتی مطابقت ندارد", + "passphraseLabel": "عبارت عبور 12 کلمه ای شما", + "recaptchaFailed": "اعتبارسنجی ReCaptcha ناموفق بود", + "register": "ساخت حساب کاربری", + "title": "عبارت عبور خود را تایید کنید" + } + }, + "errors": { + "badge": "مشکلی پیش آمده", + "details": "جزئیات خطا", + "reloadPage": "صفحه را ریلود کنید", + "showError": "نمایش جزئیات خطا", + "title": "مشکلی پیش آمده!" + }, + "footer": { + "legal": { + "disclaimer": "سلب مسئولیت", + "disclaimerText": "مووی-وب هیچ فایلی را میزبانی نمی‌کند، صرفاً به سرویس‌های شخص ثالث لینک می‌دهد. مسائل قانونی باید با میزبان‌ها و ارائه‌دهندگان فایل مطرح شود. مووی-وب مسئولیتی در قبال هیچ فایل رسانه‌ای که توسط ارائه‌دهندگان ویدیو نشان داده می‌شود ندارد." + }, + "links": { + "discord": "دیسکورد", + "dmca": "DMCA", + "github": "گیت هاب" + }, + "tagline": "فیلم و سریال مورد علاقه‌تان را با این برنامه استریم منبع باز تماشا کنید." + }, + "global": { + "name": "مووی-وب", + "pages": { + "about": "درباره ما", + "dmca": "DMCA", + "login": "ورود", + "onboarding": "راه اندازی", + "pagetitle": "{{title}} - مووی-وب", + "register": "ثبت نام", + "settings": "تنظیمات" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "علاقه مندی ها" + }, + "continueWatching": { + "sectionTitle": "ادامه تماشا" + }, + "mediaList": { + "stopEditing": "توقف ویرایش" + }, + "search": { + "allResults": "همه چیزی بود که داشتیم!", + "failed": "چیزی پیدا نشد، دوباره تلاش کنید!", + "loading": "در حال بارگذاری...", + "noResults": "متاسفانه چیزی پیدا نکردیم!", + "placeholder": "چه می‌خواهید تماشا کنید؟", + "sectionTitle": "جست و جو نتایج" + }, + "titles": { + "day": { + "default": "امروز چه می‌خواهید تماشا کنید؟", + "extra": [ + "به دنبال ماجراجویی هستید؟ \"ژوراسیک پارک\" انتخابی عالی است." + ] + }, + "morning": { + "default": "امروز چه می‌خواهید تماشا کنید؟", + "extra": ["فیلم \"پیش از طلوع\" رو می‌توانم پیشنهاد کنم"] + }, + "night": { + "default": "امشب چه می‌خواهید تماشا کنید؟", + "extra": [ + "روز سختی را گذرانده اید؟ شنیده ام که \"جن گیر\" فیلم خوبی است." + ] + } + } + }, + "media": { + "episodeDisplay": "فصل {{season}} قسمت {{episode}}", + "types": { + "movie": "فیلم", + "show": "سریال" + } + }, + "navigation": { + "banner": { + "offline": "اتصال اینترنت خود را بررسی کنید" + }, + "menu": { + "about": "درباره ما", + "donation": "حمایت", + "logout": "خروج", + "register": "همگام سازی", + "settings": "تنظیمات", + "support": "پشتیبانی" + } + }, + "notFound": { + "badge": "پیدا نشد", + "goHome": "بازگشت به خانه", + "message": "همه جا را جستجو کردیم: زیر سطل‌ها، در کمد، پشت پروکسی، اما در نهایت نتوانستیم صفحه‌ای که به دنبال آن هستید را پیدا کنیم.", + "title": "نتواستیم صفحه را پیدا کنیم" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "لغو کنید", + "confirm": "از تنظیمات پیش فرض استفاده کنید", + "description": "راه‌اندازی پیش‌فرض بهترین جریان‌ها را ندارد و می‌تواند به طور غیرقابل تحملی کند باشد.", + "title": "مطمئنی؟" + }, + "extension": { + "back": "برگرد", + "explainer": "با استفاده از افزونه مرورگر، می‌توانید بهترین جریان‌های ارائه شده را دریافت کنید. فقط با یک نصب ساده.", + "extensionHelp": "اگر افزونه را نصب کرده اید اما شناسایی نشده است. افزونه را از طریق منوی افزونه مرورگر خود باز کنید و مراحل روی صفحه را دنبال کنید.", + "status": { + "disallowed": "برنامه افزودنی برای این صفحه فعال نیست", + "disallowedAction": "افزونه را فعال کنید", + "failed": "درخواست وضعیت ناموفق بود", + "loading": "منتظر نصب افزونه هستیم", + "outdated": "نسخه افزونه خیلی قدیمی است", + "success": "برنامه افزودنی همانطور که انتظار می رود کار می کند!" + }, + "submit": "ادامه دهید", + "title": "بیایید با یک افزونه شروع کنیم" + }, + "proxy": { + "back": "برگرد", + "explainer": "با روش پروکسی می توانید با ساخت یک پراکسی سلف سرویس استریم های با کیفیت عالی دریافت کنید.", + "input": { + "errorConnection": "نمی توان به پروکسی وصل شد", + "errorInvalidUrl": "URL معتبر نیست", + "errorNotProxy": "انتظار یک پروکسی داشتم اما یک وب سایت دریافت کردم", + "label": "URL پروکسی", + "placeholder": "https://" + }, + "link": "آموزش ساخت پروکسی", + "submit": "پروکسی را ثبت کنید", + "title": "بیایید یک پروکسی جدید بسازیم" + }, + "start": { + "explainer": "برای دریافت بهترین جریان های ممکن شما باید انتخاب کنید که از کدام روش پخش جریانی استفاده کنید.", + "options": { + "default": { + "text": "من پخش جریانی با کیفیت خوب نمی‌خواهم،<0 /> <1>از تنظیمات پیش‌فرض استفاده کنید" + }, + "extension": { + "action": "افزونه را نصب کنید", + "description": "افزونه مرورگر را نصب کنید و به بهترین منابع دسترسی پیدا کنید.", + "quality": "بهترین کیفیت", + "title": "پسوند مرورگر" + }, + "proxy": { + "action": "راه اندازی پروکسی", + "description": "یک پروکسی را فقط در 5 دقیقه راه اندازی کنید و به منابع عالی دسترسی پیدا کنید.", + "quality": "کیفیت خوب", + "title": "پروکسی سفارشی" + } + }, + "title": "بیایید با فیلم وب راه اندازی کنیم" + } + }, + "overlays": { + "close": "بستن" + }, + "player": { + "back": { + "default": "بازگشت به خانه", + "short": "بازگشت" + }, + "casting": { + "enabled": "ارسال روی دستگاه..." + }, + "menus": { + "downloads": { + "disclaimer": "دانلود ها به طور مستقیم از ارائه دهنده گرفته شده است. مووی-وب کنترلی بر نحوه ارائه دانلود ها ندارد.", + "downloadPlaylist": "دانلود لیست پخش", + "downloadSubtitle": "دانلود زیرنویس فعلی", + "downloadVideo": "دانلود ویدیو", + "hlsDisclaimer": "دانلودها مستقیماً از ارائه دهنده گرفته می شوند. sudo-flix کنترلی بر نحوه ارائه دانلودها ندارد.

لطفاً توجه داشته باشید که در حال دانلود یک لیست پخش HLS هستید، اگر با فرمت های پخش جریانی پیشرفته آشنایی ندارید دانلود آن توصیه نمی شود. . منابع مختلف را برای فرمت های مختلف امتحان کنید.", + "onAndroid": { + "1": "برای دانلود در اندروید، روی دکمه دانلود کلیک کنید و سپس در صفحه جدید، روی ویدیو ضربه بزنید و نگه دارید، سپس ذخیره را انتخاب کنید.", + "shortTitle": "دانلود / اندروید", + "title": "دانلود در اندروید" + }, + "onIos": { + "1": "برای دانلود در iOS، روی دکمه دانلود کلیک کنید و سپس در صفحه جدید، روی و سپس روی Save to Files کلیک کنید.", + "shortTitle": "دانلود / iOS", + "title": "دانلود در iOS" + }, + "onPc": { + "1": "در کامپیوتر، روی دکمه دانلود کلیک کنید و سپس در صفحه جدید، روی ویدیو کلیک راست کنید و Save video as را انتخاب کنید", + "shortTitle": "دانلود / کامپیوتر", + "title": "دانلود در کامپیوتر" + }, + "title": "دانلود" + }, + "episodes": { + "button": "قسمت ها", + "emptyState": "هنوز هیچ قسمتی در این فصل منتشر نشده، بعدا سر بزنید!", + "episodeBadge": "قسمت {{episode}}", + "loadingError": "مشکلی در دریافت قسمت ها پیش آمده", + "loadingList": "در حال دریافت...", + "loadingTitle": "در حال دریافت...", + "unairedEpisodes": "یک یا چند قسمت در این فصل غیرفعال شده است به دلیل اینکه هنوز پخش نشده است." + }, + "playback": { + "speedLabel": "سرعت پخش", + "title": "تنظیمات پخش" + }, + "quality": { + "automaticLabel": "کیفیت خودکار", + "hint": "شما می توانید با <0>تغییر منبع گزینه های مختلف کیفیت را به دست آورید.", + "iosNoQuality": "با توجه به محدودیت های تعریف شده توسط اپل، انتخاب کیفیت در iOS برای این منبع در دسترس نیست. شما می توانید با <0>تغییر منبع گزینه های مختلف کیفیت را به دست آورید.", + "title": "کیفیت" + }, + "settings": { + "downloadItem": "دانلود", + "enableSubtitles": "روشن کردن زیرنویس", + "experienceSection": "تجربه مشاهده", + "playbackItem": "تنظیمات پخش", + "qualityItem": "کیفیت", + "sourceItem": "منبع ویدیو", + "subtitleItem": "تنظیمات زیرنویس", + "videoSection": "تنظیمات ویدیو" + }, + "sources": { + "failed": { + "text": "یک خطا در هنگام تلاش برای پیدا کردن فیلم وجود دارد، لطفا یک منبع دیگر را امتحان کنید.", + "title": "دریافت اطلاعات ناموفق بود" + }, + "noEmbeds": { + "text": "نتوانستیم اطلاعات را پیدا کنیم، لطفا منبعی دیگر را امتحان کنید.", + "title": "اطلاعات پیدا نشد" + }, + "noStream": { + "text": "فیلم یا سریال شما در این منبع وجود ندارد.", + "title": "استریمی وجود ندارد" + }, + "title": "منبع", + "unknownOption": "ناشناخته" + }, + "subtitles": { + "customChoice": "انتخاب زیرنویس از فایل", + "customizeLabel": "شخصی سازی", + "offChoice": "خاموش", + "settings": { + "backlink": "زیرنویس سفارشی", + "delay": "تاخیر زیرنویس", + "fixCapitals": "تصحیح حروف اول بزرگ (انگلیسی)" + }, + "title": "زیرنویس ها", + "unknownLanguage": "ناشناخته" + } + }, + "metadata": { + "api": { + "text": "داده API بارگیری نشد، لطفا اتصال اینترنت خود را بررسی کنید.", + "title": "داده API بارگیری نشد" + }, + "dmca": { + "badge": "حذف شد", + "text": "این رسانه به دلیل اعلامیه حذف یا ادعای حق نسخه‌برداری دیگر در دسترس نیست.", + "title": "رسانه حذف شده است" + }, + "extensionPermission": { + "badge": "مجوز وجود ندارد", + "button": "از پسوند استفاده کنید", + "text": "شما افزونه مرورگر را دارید، اما برای شروع استفاده از برنامه افزودنی به اجازه شما نیاز داریم.", + "title": "افزونه را پیکربندی کنید" + }, + "failed": { + "badge": "ناموفق", + "homeButton": "بازگشت به خانه", + "text": "نتوانستیم اطلاعات را از TMDB دریافت کنیم. ممکن است TMDB موقتا غیرفعال باشد یا توسط ارائه دهنده شما فیلتر باشد.", + "title": "اطلاعات دریافت نشد" + }, + "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}}" + }, + "turnstile": { + "description": "لطفا انسانیت خود را با تموم کردن چالش های کپچا به طور درست ثابت کنید. برای امن نگه داشتن فیلم وب!", + "error": "انسانیت شما تأیید نشد. لطفا دوباره تلاش کنید.", + "title": "ما باید برسی کنیم که شما انسان هستید.", + "verifyingHumanity": "تایید کردن انسانیت شما..." + } + }, + "screens": { + "dmca": { + "text": "به صفحه تماس DMCA مووی-وب خوش آمدید! ما به حقوق مالکیت معنوی احترام می گذاریم و می خواهیم به سرعت به هر گونه نگرانی کپی رایت رسیدگی کنیم. اگر فکر می کنید کار کپی رایت شما به طور نادرست در پلت فرم ما استفاده شده است، لطفا یک اطلاعیه DMCA دقیق را به ایمیل زیر ارسال کنید. لطفا شرح مواد کپی رایت، جزئیات تماس خود و بیانیه ای از اعتقاد حسن نیت را شامل شوید. ما متعهد به حل و فصل سریع این مسائل هستیم و از همکاری شما در حفظ وب فیلم به عنوان مکانی که به خلاقیت و کپی رایت احترام می گذارند، قدردانی می کنیم.", + "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": "ظاهر" + }, + "connections": { + "server": { + "description": "اگر میخواهید به یک بک-اند سفارشی برای ذخیره داده متصل شوید، با فعال و ارائه کردن این لینک ادامه دهید. <0>دستورالعمل ها.", + "label": "سرور سفارشی", + "urlLabel": "URL سرور سفارشی" + }, + "setup": { + "doSetup": "راه اندازی را انجام دهید", + "errorStatus": { + "description": "به نظر می رسد که یک یا چند مورد در این تنظیم نیاز به توجه شما دارد.", + "title": "چیزی به توجه شما نیاز دارد" + }, + "itemError": "مشکلی در این تنظیم وجود دارد. برای رفع آن دوباره تنظیمات را انجام دهید.", + "items": { + "default": "تنظیم پیش فرض", + "extension": "افزونه", + "proxy": "پروکسی سفارشی" + }, + "redoSetup": "تنظیم مجدد", + "successStatus": { + "description": "همه چیز برای شروع تماشای رسانه مورد علاقه‌تان آماده است.", + "title": "همه چیز تنظیم شده است!" + }, + "unsetStatus": { + "description": "لطفاً روی دکمه سمت راست کلیک کنید تا فرآیند نصب شروع شود.", + "title": "شما راه‌اندازی را طی نکرده‌اید" + } + }, + "title": "اتصالات", + "workers": { + "addButton": "اضافه کردن worker جدید", + "description": "برای ایجاد عملکرد برنامه، تمام ترافیک از طریق پروکسی ها هدایت می شود. اگر میخواهید این کار انجام دهید حتما از worker های خودتان استفاده کنید. <0>دستورالعمل ها.", + "emptyState": "هنوز هیچ worker ای وجود ندارد، یکی اضافه کنید", + "label": "استفاده از worker های پروکسی سفارشی", + "urlLabel": "لینک worker ها", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "زبان برنامه", + "languageDescription": "زبان برای کل برنامه کاربردی است.", + "thumbnail": "ایجاد تصاویر کوچک", + "thumbnailDescription": "بیشتر اوقات، ویدیوها تصاویر کوچک ندارند. شما می توانید این تنظیم را فعال کنید تا آنها را در لحظه تولید کنید، اما آنها می توانند ویدیوی شما را کندتر کنند.", + "thumbnailLabel": "ایجاد تصاویر کوچک", + "title": "اولویت ها" + }, + "reset": "بازنشانی", + "save": "ذخیره", + "sidebar": { + "info": { + "appVersion": "نسخه برنامه", + "backendUrl": "لینک بک-اند", + "backendVersion": "نسخه بک-اند", + "hostname": "لینک برنامه", + "insecure": "نا امن", + "notLoggedIn": "شما وارد نشده اید", + "secure": "امن", + "title": "اطلاعات برنامه", + "unknownVersion": "ناشناخته", + "userId": "شناسه کاربری" + } + }, + "subtitles": { + "backgroundLabel": "شفافیت پس‌زمینه", + "colorLabel": "رنگ", + "previewQuote": "نباید بترسم. ترس قاتل ذهن است.", + "textSizeLabel": "اندازه متن", + "title": "زیرنویس" + }, + "unsaved": "تغییرات ذخیره نشده دارید" + } +} diff --git a/src/assets/locales/fi-FI.json b/src/assets/locales/fi-FI.json new file mode 100644 index 00000000..069a3ab3 --- /dev/null +++ b/src/assets/locales/fi-FI.json @@ -0,0 +1,290 @@ +{ + "about": { + "description": "movie-web on verkkosovellus, joka etsii suoratoistoja internetistä. Tiimi pyrkii enimmäkseen minimalistiseen lähestymistapaan sisällön kuluttamiseen.", + "faqTitle": "Yleisiä kysymyksiä", + "q1": { + "body": "movie-web ei isännöi mitään sisältöä. Kun klikkaat jotain katsottavaa, valittua mediaa etsitään internetistä (latausnäytössä ja 'videolähteet'-välilehdellä näet, mitä lähdettä käytät). movie-web ei koskaan lataa mediaa, kaikki tapahtuu tämän hakumekanismin kautta.", + "title": "Mistä sisältö tulee?" + }, + "q2": { + "body": "Ohjelmaa tai elokuvaa ei voi pyytää, movie-web ei hallinnoi sisältöä. Kaikki sisältö katsotaan internetin lähteistä.", + "title": "Missä voin pyytää sarjaa tai elokuvaa?" + }, + "q3": { + "body": "Hakutuloksemme perustuvat The Movie Database (TMDB) -tietokantaan, ja ne näkyvät riippumatta siitä, onko sisältöä oikeasti lähteissämme.", + "title": "Hakutuloksissa näytetään ohjelma tai elokuva. Miksi en voi toistaa sitä?" + }, + "title": "Tietoja movie-webistä" + }, + "actions": { + "copied": "Kopioitu", + "copy": "Kopioi" + }, + "auth": { + "createAccount": "Eikö sinulla ole vielä tiliä? <0>Luo tili.", + "deviceNameLabel": "Laitteen nimi", + "deviceNamePlaceholder": "Henkilökohtainen puhelin", + "generate": { + "description": "Tunnuslauseesi toimii käyttäjätunnuksena ja salasanana. Varmista, että pidät sen turvassa, sillä sinun on annettava se kirjautuaksesi tilillesi", + "next": "Olen tallentanut tunnuslauseeni", + "passphraseFrameLabel": "Tunnuslause", + "title": "Sinun tunnuslause" + }, + "hasAccount": "Onko sinulla jo tili? <0>Kirjaudu sisään tästä.", + "login": { + "description": "Anna tunnuslauseesi kirjautuaksesi tilillesi", + "deviceLengthError": "Anna laitteen nimi", + "passphraseLabel": "12-sanainen tunnuslause", + "passphrasePlaceholder": "Tunnuslause", + "submit": "Kirjaudu sisään", + "title": "Kirjaudu tilillesi", + "validationError": "Väärä tai puutteellinen tunnuslause" + }, + "register": { + "information": { + "color1": "Profiilin väri yksi", + "color2": "Profiilin väri kaksi", + "header": "Anna laitteellesi nimi ja valitse haluamasi värit ja käyttäjäkuvake", + "icon": "Käyttäjäkuvake", + "next": "Seuraava", + "title": "Tilitiedot" + } + }, + "trust": { + "failed": { + "text": "Oletko määrittänyt sen oikein?", + "title": "Palvelimeen ei saada yhteyttä" + }, + "host": "Olet muodostamassa yhteyttä <0>{{hostname}} - vahvista, että luotat siihen ennen kuin luot tilin", + "no": "Mene takaisin", + "title": "Luotatko tähän palvelimeen?", + "yes": "Luotan tähän palvelimeen" + }, + "verify": { + "description": "Anna aikaisemmin saamasi tunnuslause vahvistaaksesi, että olet tallentanut sen ja luodaksesi tilisi", + "invalidData": "Tiedot eivät kelpaa", + "noMatch": "Tunnuslause ei täsmää", + "passphraseLabel": "12-sanainen tunnuslauseesi", + "recaptchaFailed": "ReCaptcha-tarkistus epäonnistui", + "register": "Luo tili", + "title": "Vahvista tunnuslauseesi" + } + }, + "errors": { + "badge": "Se hajosi", + "details": "Virheen tiedot", + "reloadPage": "Lataa sivu uudelleen", + "showError": "Näytä virheen tiedot", + "title": "Havaitsimme virheen!" + }, + "footer": { + "legal": { + "disclaimer": "Vastuuvapauslauseke", + "disclaimerText": "movie-web ei isännöi tiedostoja, se vain linkittää kolmannen osapuolen palveluihin. Lakiasioista tulee ottaa yhteyttä tiedostoisäntään ja palveluntarjoajiin. movie-web ei ole vastuussa videontarjoajien näyttämistä mediatiedostoista." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Katso suosikkiohjelmiasi ja elokuviasi tällä avoimen lähdekoodin suoratoistosovelluksella." + }, + "global": { + "name": "movie-web", + "pages": { + "about": "Meistä", + "dmca": "DMCA", + "login": "Kirjaudu sisään", + "onboarding": "asetus", + "pagetitle": "{{title}} - movie-web", + "register": "Rekisteröidy", + "settings": "Asetukset" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Kirjanmerkit" + }, + "continueWatching": { + "sectionTitle": "Jatka katselua" + }, + "mediaList": { + "stopEditing": "Lopeta muokkaaminen" + }, + "search": { + "allResults": "Siinä kaikki mitä meillä on!", + "failed": "Mediaa ei löytynyt, yritä uudelleen!", + "loading": "Ladataan...", + "noResults": "Emme löytäneet mitään!", + "placeholder": "Mitä haluat katsoa?", + "sectionTitle": "Hakutulokset" + }, + "titles": { + "day": { + "default": "Mitä haluaisit katsoa tänä iltapäivänä?", + "extra": [ + "Onko seikkailunhaluinen olo? Jurassic Park saattaa olla täydellinen valinta." + ] + }, + "morning": { + "default": "Mitä haluaisit katsoa tänä aamuna?", + "extra": [ + "Kuulen, että Rakkautta ennen aamua (Before Sunrise) on hyvä" + ] + }, + "night": { + "default": "Mitä haluaisit katsoa tänä iltana?", + "extra": [ + "Väsynyt? Kuulin, että Manaaja (The Exorcist) on hyvä." + ] + } + } + }, + "media": { + "episodeDisplay": "K{{season}} J{{episode}}", + "types": { + "movie": "Elokuva", + "show": "Sarja" + } + }, + "navigation": { + "banner": { + "offline": "Tarkista Internet-yhteytesi" + }, + "menu": { + "about": "Meistä", + "donation": "Lahjoita", + "logout": "Kirjaudu ulos", + "register": "Synkronoi pilveen", + "settings": "Asetukset", + "support": "Tuki" + } + }, + "notFound": { + "badge": "Ei löydetty", + "goHome": "Takaisin kotiin", + "message": "Etsimme kaikkialta: roskakorien alta, kaapista, välityspalvelimen takaa, mutta emme lopulta löytäneet etsimääsi sivua.", + "title": "Sivua ei löytynyt" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Peruuta", + "confirm": "Käytä oletuksia", + "description": "Oletuksissa ei ole parhaita suoratoistoja ja se voi olla sietämättömän hidasta.", + "title": "Oletko varma?" + }, + "extension": { + "back": "Mene takaisin", + "explainer": "Käyttämällä selainlaajennusta voit saada parhaat tarjoamamme suoratoistot. Yksinkertaisella asennuksella.", + "explainerIos": "Valitettavasti selainlaajennusta ei tueta iOS:ssä. Valitse toinen vaihtoehto painamalla Palaa.", + "extensionHelp": "Jos olet asentanut laajennuksen, mutta se ei havaitse sitä, avaa laajennus selaimen laajennusvalikosta ja noudata näytön ohjeita.", + "linkChrome": "Asenna Chromen laajennus", + "linkFirefox": "Asenna Firefoxin laajennus", + "notDetecting": "Asensitko sen Chromelle, mutta sivusto ei havaitse sitä? Kokeile ladata sivu uudelleen!", + "notDetectingAction": "Lataa sivu uudelleen", + "status": { + "disallowed": "Laajennus ei ole otettu käyttöön tälle sivulle", + "disallowedAction": "Ota laajennus käyttöön", + "failed": "Pyynnön tilan hakeminen epäonnistui", + "loading": "Odottaa, että asennat laajennuksen", + "outdated": "Laajennuksen versio on liian vanha", + "success": "Laajennus toimii odotetusti!" + }, + "submit": "Jatketaan", + "title": "Aloitetaan laajennuksella" + }, + "proxy": { + "back": "Mene takaisin", + "explainer": "Proxy-menetelmällä voit saada erinomaisen laadukkaita suoratoistoja luomalla itsepalveluvaltuutetun proxyn.", + "input": { + "errorConnection": "Yhteys proxyn kanssa ei onnistunut", + "errorInvalidUrl": "Ei kelvollinen URL-osoite", + "errorNotProxy": "Odotettiin proxya, mutta saatiinkin verkkosivu", + "label": "Proxyn URL-osoite", + "placeholder": "https://" + }, + "link": "Opi luomaan proxy", + "submit": "Toimita proxy", + "title": "Luodaan uusi proxy" + }, + "start": { + "explainer": "Parhaiden suoratoistojen saamiseksi sinun täytyy valita, mitä suoratoistomenetelmää haluat käyttää.", + "options": { + "default": { + "text": "En halua hyvälaatuisia suoratoistoja,<0 /> <1>käytä oletusasetusta" + }, + "extension": { + "action": "Asenna laajennus", + "description": "Asenna selaimen laajennus ja saa pääsy parhaisiin lähteisiin." + } + }, + "title": "Aloitetaan asennus elokuva-webin kanssa" + } + }, + "player": { + "menus": { + "episodes": { + "emptyState": "Tässä kaudessa ei ole jaksoja, tarkista myöhemmin!" + }, + "quality": { + "hint": "Voit kokeilla <0>vaihtaa lähdettä saadaksesi eri laatuasetuksia." + }, + "sources": { + "noEmbeds": { + "text": "Emme löytäneet upotuksia, kokeile toista lähdettä." + }, + "noStream": { + "text": "Tässä lähteessä ei ole suoratoistoja tälle elokuvalle tai ohjelmalle." + } + } + }, + "metadata": { + "api": { + "text": "API-metatietoja ei voitu ladata, tarkista internet-yhteys.", + "title": "Ei voitu ladata API:n metatietoja." + } + }, + "playbackError": { + "errors": { + "errorAborted": "Mediatiedon haku keskeytettiin käyttäjän pyynnöstä.", + "errorNotSupported": "Mediaa tai mediantarjoajaa ei tueta." + }, + "text": "Tapahtui virhe yritettäessä toistaa mediaa. Yritä uudelleen." + }, + "time": { + "remaining": "{{timeLeft}} jäljellä • Päättyy {{timeFinished, datetime}}" + }, + "turnstile": { + "error": "Ei voitu varmistaa inhimillisyyttäsi. Yritä uudelleen.", + "title": "Meidän täytyy varmistaa, että olet ihminen." + } + }, + "screens": { + "migration": { + "inProgress": "Pysy hetkisen odotuksessa, siirrämme tietojasi. Tämä ei kestä kauan." + } + }, + "settings": { + "account": { + "register": { + "text": "Jaa katseluetenemisesi laitteiden välillä ja pidä ne synkronoituina." + } + }, + "connections": { + "setup": { + "unsetStatus": { + "description": "Käynnistä asennusprosessi napsauttamalla oikealla olevaa painiketta." + } + }, + "workers": { + "emptyState": "Ei vielä työntekijöitä, lisää yksi alle" + } + }, + "preferences": { + "languageDescription": "Kieli sovellettu koko sovellukseen." + }, + "subtitles": { + "previewQuote": "Minun ei pidä pelätä. Pelko on mielen tappaja." + } + } +} diff --git a/src/assets/locales/fr.json b/src/assets/locales/fr.json new file mode 100644 index 00000000..cabaf2ad --- /dev/null +++ b/src/assets/locales/fr.json @@ -0,0 +1,530 @@ +{ + "about": { + "description": "sudo-flix 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": "sudo-flix 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 sudo-flix, tout passe par ce mécanisme de recherche.", + "title": "D'où vient le contenu ?" + }, + "q2": { + "body": "Il est impossible de solliciter une émission ou un film car sudo-flix ne gère aucun contenu. Les sources sur Internet sont utilisées pour consulter tous les contenus.", + "title": "Où puis-je demander une série ou un film ?" + }, + "q3": { + "body": "Que nos sources soient propriétaires du contenu ou non, The Movie Database (TMDB) fournit et affiche nos résultats de recherche.", + "title": "Les résultats de la recherche affichent l'émission ou le film, pourquoi ne puis-je pas le lire ?" + }, + "title": "À propos de sudo-flix" + }, + "actions": { + "copied": "Copié", + "copy": "Copier" + }, + "auth": { + "createAccount": "N'avez-vous pas encore de compte ? <0>Créer un compte.", + "deviceNameLabel": "Nom de l'appareil", + "deviceNamePlaceholder": "Téléphone personnel", + "generate": { + "description": "Le nom d'utilisateur et le mot de passe sont obtenus à partir de votre passphrase. Vous devrez la saisir pour accéder à votre compte, alors gardez-la précieusement", + "next": "J'ai sauvegardé ma passphrase", + "passphraseFrameLabel": "Pass phrase", + "title": "Votre passphrase" + }, + "hasAccount": "Avez-vous déjà un compte ? <0>Connectez-vous ici.", + "login": { + "description": "Veuillez fournir votre passphrase pour accéder à votre compte", + "deviceLengthError": "Veuillez saisir un nom d'appareil", + "passphraseLabel": "Passphrase de 12 mots", + "passphrasePlaceholder": "Phrase secrète", + "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}} - veuillez confirmer que vous lui faites confiance avant de créer un compte", + "no": "Retour", + "title": "Avez-vous confiance en 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": "Le site sudo-flix ne stocke pas de fichiers, mais propose des liens vers des services externes. Les problèmes juridiques doivent être traités avec les fournisseurs et les hébergeurs de fichiers. Les fichiers multimédias diffusés par les fournisseurs de vidéos ne sont pas couverts par sudo-flix." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Cette application de streaming open source vous permet de regarder vos émissions et films préférés." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "À propos", + "dmca": "DMCA", + "login": "Se connecter", + "onboarding": "Setup", + "pagetitle": "{{title}} - sudo-flix", + "register": "Créer un compte", + "settings": "Paramètres" + } + }, + "home": { + "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 ?", + "extra": [ + "Amoureux des films d'animation ? Suzume est un incontournable." + ] + }, + "morning": { + "default": "Que voulez-vous regarder ce matin ?", + "extra": ["Les films, c'est comme les voyages : ça nous ouvre l'esprit"] + }, + "night": { + "default": "Que voulez-vous regarder ce soir ?", + "extra": [ + "À la recherche d'un bon film d'horreur à regarder ? J'ai entendu dire que Scream était bien." + ] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Série" + } + }, + "navigation": { + "banner": { + "offline": "Vérifiez votre connexion internet" + }, + "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", + "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" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Annuler", + "confirm": "Utiliser la configuration de départ", + "description": "La configuration par défaut n'offre pas les meilleurs flux et peut être insupportablement lente.", + "title": "Es-tu sûr ?" + }, + "extension": { + "back": "Retour en arrière", + "explainer": "En utilisant l'extension de navigateur, vous pouvez obtenir les meilleurs flux que nous avons à offrir. Avec juste une simple installation.", + "extensionHelp": "Si vous avez installé l'extension mais qu'elle n'est pas détectée. Ouvrez l'extension via le menu des extensions de votre navigateur et suivez les étapes à l'écran.", + "status": { + "disallowed": "L'extension n'est pas activée pour cette page", + "disallowedAction": "Activer l'extension", + "failed": "Échec de la demande de statut", + "loading": "En attendant que vous installiez l'extension", + "outdated": "Version d'extension trop ancienne", + "success": "L'extension fonctionne comme prévu !" + }, + "submit": "Continuer", + "title": "Commençons par une extension" + }, + "proxy": { + "back": "Retour en arrière", + "explainer": "Avec la méthode du proxy, vous pouvez obtenir des flux de bonne qualité en créant un proxy en libre-service.", + "input": { + "errorConnection": "Impossible de se connecter au proxy", + "errorInvalidUrl": "URL non valide", + "errorNotProxy": "Je m'attendais à un proxy mais j'ai obtenu un site Web", + "label": "URL du proxy", + "placeholder": "https://" + }, + "link": "Apprenez à créer un proxy", + "submit": "Soumettre le proxy", + "title": "Créons un nouveau proxy" + }, + "start": { + "explainer": "Pour obtenir les meilleurs flux possibles. Vous devrez choisir la méthode de streaming que vous souhaitez utiliser.", + "options": { + "default": { + "text": "Je ne veux pas de flux de bonne qualité,<0 /> <1>use the default setup" + }, + "extension": { + "action": "Installer l'extension", + "description": "Installez l'extension de navigateur et accédez aux meilleures sources.", + "quality": "Meilleur qualité", + "title": "Extension du navigateur" + }, + "proxy": { + "action": "Configurez le proxy", + "description": "Configurez un proxy en seulement 5 minutes et accédez à d'excellentes sources.", + "quality": "Bonne qualité", + "title": "Proxy personnalisé" + } + }, + "title": "Commençons par vous configurer sudo-flix" + } + }, + "overlays": { + "close": "Fermer" + }, + "player": { + "back": { + "default": "Retour à la page d'accueil", + "short": "Retour" + }, + "casting": { + "enabled": "Casting à l'appareil..." + }, + "menus": { + "downloads": { + "disclaimer": "Les téléchargements sont effectués directement par le fournisseur. sudo-flix n'a aucun contrôle sur la manière dont les téléchargements sont effectués.", + "downloadPlaylist": "Télécharger la liste de lecture", + "downloadSubtitle": "Télécharger les sous-titres", + "downloadVideo": "Télécharger la vidéo", + "hlsDisclaimer": "Les téléchargements sont effectués directement auprès du fournisseur. sudo-flix n'a aucun contrôle sur la façon dont les téléchargements sont fournis.

Veuillez noter que vous téléchargez une liste de lecture HLS, il n'est pas recommandé de télécharger si vous n'êtes pas familier avec les formats de streaming avancés. . Essayez différentes sources pour différents formats.", + "onAndroid": { + "1": "Pour télécharger sur Android, cliquez sur le bouton de téléchargement puis, sur la nouvelle page, tapez et maintenez sur la vidéo, puis sélectionnez enregistrer.", + "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 , puis Enregistrer dans les fichiers .", + "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 Enregistrer la vidéo sous", + "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...", + "unairedEpisodes": "Un ou plusieurs épisodes de cette saison ont été désactivés car ils n'ont pas encore été diffusés." + }, + "playback": { + "speedLabel": "Vitesse de lecture", + "title": "Paramètres de lecture" + }, + "quality": { + "automaticLabel": "Qualité automatique", + "hint": "Vous pouvez essayer de <0>changer de fournisseur 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 pour obtenir des options de qualité différentes.", + "title": "Qualité" + }, + "settings": { + "downloadItem": "Télécharger", + "enableSubtitles": "Activer les sous-titres", + "experienceSection": "Expérience de visionnage", + "playbackItem": "Paramètres de lecture", + "qualityItem": "Qualité", + "sourceItem": "Sources vidéo", + "subtitleItem": "Paramètres des sous-titres", + "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" + }, + "subtitles": { + "customChoice": "Importer des sous-titres depuis un fichier", + "customizeLabel": "Personnaliser", + "offChoice": "Désactivé", + "settings": { + "backlink": "Sous-titres personnalisés", + "delay": "Délai des sous-titres", + "fixCapitals": "Corriger les capitales" + }, + "title": "Sous-titres", + "unknownLanguage": "Inconnu" + } + }, + "metadata": { + "api": { + "text": "Impossible de charger les métadonnées de l'API, veuillez vérifier votre connexion Internet.", + "title": "Échec du chargement des métadonnées de l'API" + }, + "dmca": { + "badge": "Supprimé", + "text": "Ce média n'est plus disponible en raison d'un avis de retrait ou d'une réclamation pour atteinte aux droits d'auteur.", + "title": "Le média a été supprimé" + }, + "extensionPermission": { + "badge": "Autorisation manquante", + "button": "Utiliser l'extension", + "text": "Vous disposez de l'extension de navigateur, mais nous avons besoin de votre autorisation pour commencer à utiliser l'extension.", + "title": "Configurer l'extension" + }, + "failed": { + "badge": "Échec", + "homeButton": "Revenir à l'accueil", + "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}}" + }, + "turnstile": { + "description": "Veuillez vérifier que vous êtes bien humain en complétant le Captcha à droite. C'est pour assurer la sécurité de sudo-flix !", + "error": "Échec de la vérification de votre humanité. Veuillez réessayer.", + "title": "Nous devons vérifier que vous êtes un humain.", + "verifyingHumanity": "Vérification de votre humanité..." + } + }, + "screens": { + "dmca": { + "text": "Bienvenue sur la page de contact DMCA de sudo-flix ! 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 sudo-flix 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" + }, + "connections": { + "server": { + "description": "Si vous désirez utiliser un système de stockage externe pour enregistrer vos données, activez cette option et indiquez l'URL. <0>Instructions.", + "label": "Serveur personnalisé", + "urlLabel": "URL du serveur personnalisé" + }, + "setup": { + "doSetup": "Faire la configuration", + "errorStatus": { + "description": "Il semble qu'un ou plusieurs éléments de cette configuration nécessitent votre attention.", + "title": "Quelque chose nécessite votre attention" + }, + "itemError": "Ce paramètre présente un problème. Résolvez le problème en redémarrant la configuration.", + "items": { + "default": "Configuration par défaut", + "extension": "Extension", + "proxy": "Proxy personnalisé" + }, + "redoSetup": "Refaire la configuration", + "successStatus": { + "description": "Tout est réuni pour que vous puissiez commencer à regarder vos médias préférés.", + "title": "Tout est mis en place !" + }, + "unsetStatus": { + "description": "Pour commencer le processus de configuration, veuillez cliquer sur le bouton à droite.", + "title": "Vous n'avez pas fait la configuration" + } + }, + "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. <0>Instructions.", + "emptyState": "Pas encore de workers, ajoutez-en un ci-dessous", + "label": "Utiliser des agents proxy personnalisés", + "urlLabel": "URLs des workers", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Language de l'application", + "languageDescription": "Langue appliquée à l’ensemble de l’application.", + "thumbnail": "Générer des miniatures", + "thumbnailDescription": "La plupart du temps, les vidéos n'ont pas de miniatures. Vous pouvez activer ce paramètre pour les générer à la volée, mais ils peuvent ralentir votre vidéo.", + "thumbnailLabel": "Générer des miniatures", + "title": "Préférences" + }, + "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" + } + }, + "subtitles": { + "backgroundLabel": "Opacité du fond", + "colorLabel": "Couleur", + "previewQuote": "Plus l'obscurité est profonde, plus la lumière brille.", + "textSizeLabel": "Taille des textes", + "title": "Sous-titres" + }, + "unsaved": "Vous avez des changements non sauvegardés" + } +} diff --git a/src/assets/locales/gl.json b/src/assets/locales/gl.json new file mode 100644 index 00000000..8adecabb --- /dev/null +++ b/src/assets/locales/gl.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "sudo-flix é unha aplicación web que busca transmisións na rede. O equipo ten como obxectivo manter un enfoque principalmente minimalista para consumir os contidos.", + "faqTitle": "Preguntas frecuentes", + "q1": { + "body": "sudo-flix non aloxa ningún contido. Cando premes en algo para ver o contenido, búscase en internet o medio seleccionado. (Na pantalla de carga e na lapela 'fontes de video' podes ver que fonte se está a empregar. O contido nunca se carga en sudo-flix, todo realízase a través deste método de busca.", + "title": "De onde proveñen os contidos?" + }, + "q2": { + "body": "Non é posible solicitar unha película. sudo-flix non xestiona ningún contido. Todo o contido é xestionado a través de fontes na rede.", + "title": "Onde poido solicitar unha película a engadir?" + }, + "q3": { + "body": "Os nosos resultados de busqueda proveñen de The Movie Database (TMDB) e se mostran independentemente de se as nosas fontes multimedia teñen realmente o contido.", + "title": "Os resultados da busca mostran a serie ou película... Por qué non poido reproducila?" + }, + "title": "Acerca de sudo-flix" + }, + "actions": { + "copied": "Copiado", + "copy": "Copiar" + }, + "auth": { + "createAccount": "Non tes unha conta aínda? <0>Crea unha conta.", + "deviceNameLabel": "Nome do dispositivo", + "deviceNamePlaceholder": "Teléfono persoal", + "generate": { + "description": "A túa contraseña actua como o teu nome de usuario e contraseña. Asegúrate de mantelas seguras, xa que as necesitarás para iniciar sesión na túa conta", + "next": "Gardei a contraseña exitosamente", + "passphraseFrameLabel": "Contraseña", + "title": "A túa contraseña" + }, + "hasAccount": "Tes xa unha conta? <0>Inicia sesión aquí.", + "login": { + "description": "Por favor, ingresa a túa contraseña para iniciar sesión na túa conta", + "deviceLengthError": "Por favor, ingresa un nome de dispositivo", + "passphraseLabel": "Contraseña de 12 caracteres", + "passphrasePlaceholder": "Contraseña", + "submit": "Iniciar sesión", + "title": "Inicia sesión na túa conta", + "validationError": "Contraseña incorrecta ou incompleta" + }, + "register": { + "information": { + "color1": "Cór de perfil un", + "color2": "Cór de perfil dous", + "header": "Ingresa un nome para o teu dispositivo, elixe cores, e un icono de usuario", + "icon": "Ícono de usuario", + "next": "Seguinte", + "title": "Información da conta" + } + }, + "trust": { + "failed": { + "text": "Configurachelo correctamente?", + "title": "Non se puido conectar ao servidor" + }, + "host": "Estaste a conectar a <0>{{hostname}} - por favor, confirma se confías antes de crear a conta", + "no": "Regresar", + "title": "Confías neste servidor?", + "yes": "Si, si que confío neste servidor" + }, + "verify": { + "description": "Por favor, ingresa a túa contraseña para confirmar que está gardada para crear a túa conta", + "invalidData": "Os datos non son válidos", + "noMatch": "A contraseña non coincide", + "passphraseLabel": "A contraseña debe de ser de 12 caracteres", + "recaptchaFailed": "A validación ReCaptcha fallou", + "register": "Crear conta", + "title": "Confirma a túa contraseña" + } + }, + "errors": { + "badge": "Rompeu", + "details": "Detalles do erro", + "reloadPage": "Recargar a páxina", + "showError": "Mostrar detalles do erro", + "title": "Atopamos un erro!" + }, + "footer": { + "legal": { + "disclaimer": "Descargo de responsabilidade", + "disclaimerText": "sudo-flix non aloxa ningún arquivo, simplemente enlaza con servizos de terceiros. Os problemas legais deben ser tratados cós proovedores de arquivos e servizos. sudo-flix non se fai responsable dos arquivos multimedia mostrados polos provedores de video." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Disfruta das túas series e películas favoritas con esta aplicación de transmisión de código aberto." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Acerca de", + "dmca": "DMCA", + "login": "Iniciar sesión", + "pagetitle": "{{title}} - sudo-flix", + "register": "Rexistrarse", + "settings": "Configuración" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Marcadores" + }, + "continueWatching": { + "sectionTitle": "Continuar vendo" + }, + "mediaList": { + "stopEditing": "Deter edición" + }, + "search": { + "allResults": "Esto é todo o que temos!", + "failed": "Error ao encontrar contido... intentao de novo!", + "loading": "Cargando...", + "noResults": "Non atopamos nada!", + "placeholder": "Que che gustaría ver?", + "sectionTitle": "Resultados da busca" + }, + "titles": { + "day": { + "default": "Que che gustaría ver esta tarde?", + "extra": [ + "Sínteste aventureiro? Jurassic Park podería ser a elección perfecta." + ] + }, + "morning": { + "default": "Que che gustaría ver esta mañá?", + "extra": ["Escoitei que “Antes del amanecer” é boa"] + }, + "night": { + "default": "Que che gustaría ver esta noite?", + "extra": ["Canso? Escoitei que “El Exorcista” é boa."] + } + } + }, + "media": { + "episodeDisplay": "T{{season}} E{{episode}}", + "types": { + "movie": "Película", + "show": "Serie" + } + }, + "navigation": { + "banner": { + "offline": "Verifica a túa conexión a internet" + }, + "menu": { + "about": "Acerca de nós", + "donation": "Doar", + "logout": "Cerrar sesión", + "register": "Sincronizar coa nube", + "settings": "Configuración", + "support": "Soporte" + } + }, + "notFound": { + "badge": "Non atopado", + "goHome": "Volver ao inicio", + "message": "Prometocho, buscamos en todas partes: debaixo dos contenedores, no armario, detrás do proxy, pero ao final non puidemos atopar a páxina que estabas buscando.", + "title": "Non atopei a páxona que estabas a buscar" + }, + "overlays": { + "close": "Cerrar" + }, + "player": { + "back": { + "default": "Volver ao inicio", + "short": "Volver" + }, + "casting": { + "enabled": "Transmitiendo ao dispositivo..." + }, + "menus": { + "downloads": { + "disclaimer": "As descargas proveñen do provedor. sudo-flix non ten control sobre as descargas e a súa procedencia.", + "downloadPlaylist": "Descargar lista", + "downloadSubtitle": "Descargar subtítulos actuais", + "downloadVideo": "Descargar video", + "hlsDisclaimer": "As descargas realizanse directamente dende o proovedor. sudo-flix non ten control sobre como se xestionan as descargas. Ten en conta que estás a descargar unha lista de reproducción HLS, dirixidos a usuarios familiarizados coa transmisión multimedia avanzada.", + "onAndroid": { + "1": "Para descargar en Android, fai click no botón de descarga e despois, na nova páxina, mantén presionadoo vídeo e selecciona gardar.", + "shortTitle": "Descargar / Android", + "title": "Descargando en Android" + }, + "onIos": { + "1": "Para descargar en iOS, fai clic no botón de descarga e despois, na nova páxina, fai click en , e despois Gardar en archivos .", + "shortTitle": "Descargar / iOS", + "title": "Descargando en iOS" + }, + "onPc": { + "1": "Nunha PC, fai click no botón de descargas e despois, na nova páxina, fai click dereito no video e selecciona Gardar vídeo como...", + "shortTitle": "Descargar / PC", + "title": "Descargando en PC" + }, + "title": "Descargar" + }, + "episodes": { + "button": "Episodios", + "emptyState": "Non hai episodios nesta temporada, Intentao máis tarde!", + "episodeBadge": "E{{episode}}", + "loadingError": "Error cargando a sesión", + "loadingList": "Cargando...", + "loadingTitle": "Cargando...", + "unairedEpisodes": "Un ou máis episodios nesta temporada foron desactivados porque non sairon aínda." + }, + "playback": { + "speedLabel": "Velocidade de reproducción", + "title": "Configuración de reproducción" + }, + "quality": { + "automaticLabel": "Calidade automática", + "hint": "Podes intentar <0>cambiar de fonte para obter diferentes opcións de calidade.", + "iosNoQuality": "Debido a limitacións definidas por Apple, a selección de calidade no está disponible en iOS para esta fonte. Podes intentar <0>cambiar a outra fonte para obter diferentes opcións de calidade.", + "title": "Calidade" + }, + "settings": { + "downloadItem": "Descargar", + "enableSubtitles": "Activar subtítulos", + "experienceSection": "Configuración de experiencia", + "playbackItem": "Configuración do playback", + "qualityItem": "Calidade", + "sourceItem": "Fonte do video", + "subtitleItem": "Configuración dos subtítulos", + "videoSection": "Configuración de video" + }, + "sources": { + "failed": { + "text": "Acaba de producirse un erro ao intentar atopar videos, por favor, intenta cunha fonte distinta.", + "title": "Erro ao retirar" + }, + "noEmbeds": { + "text": "Non puidemos atopar ningún embed, por favor, intenta cunha fonte diferente.", + "title": "No se atoparon embeds" + }, + "noStream": { + "text": "Nesta fonte non hai contidos sobre esta película ou episodio.", + "title": "Sin fonte" + }, + "title": "Fontes", + "unknownOption": "Descoñecido" + }, + "subtitles": { + "customChoice": "Seleccionar subtítulos dende o arquivo", + "customizeLabel": "Personalizar", + "offChoice": "Apagar", + "settings": { + "backlink": "Subtítulos personalizados", + "delay": "Retardo dos subtítulos", + "fixCapitals": "Arreglar capitalización" + }, + "title": "Subtítulos", + "unknownLanguage": "Descoñecido" + } + }, + "metadata": { + "api": { + "text": "Non puiden cargar os metadatos da API, por favor, comproba a túa conexión a internet.", + "title": "Non foi posible cargar os metadatos da API" + }, + "failed": { + "badge": "Erro", + "homeButton": "Ir ao inicio", + "text": "Non se puideron cargar os metadatos do contido de TMDB. Por favor, verifica se TMDB está caído ou bloqueado na túa conexión a internet.", + "title": "Error ao cargar os metadatos" + }, + "notFound": { + "badge": "Non atopado", + "homeButton": "Volver ao inicio", + "text": "Non puidemos encontrar o contenido que solicitache. Xa seña que se eliminara ou modificara a URL.", + "title": "No se pudo atopar ese contenido." + } + }, + "nextEpisode": { + "cancel": "Cancelar", + "next": "Seguinte episodio" + }, + "playbackError": { + "badge": "Error de reproducción", + "errors": { + "errorAborted": "A obtención do contido foi cancelada pola solicitude do usuario.", + "errorDecode": "A pesar de ser determinado previamente como utilizable, produciuse un erro ao intentar decodificar o recurso do contido, o que resultou nun erro.", + "errorGenericMedia": "Produxose un erro descoñecido no contido.", + "errorNetwork": "Produxose un erro de rede que impidideu obter o contido de maneira exitosa, a pesar de estar disponible anteriormente.", + "errorNotSupported": "O contido ou o proovedor do contido non é compatible." + }, + "homeButton": "Ir ao inicio", + "text": "Produxose un erro ao intentar reproducir o contenido. Por favor, inténtao de novo.", + "title": "Non se puido reproducir o video!" + }, + "scraping": { + "items": { + "failure": "Ocurreu un erro", + "notFound": "Non ten o video", + "pending": "Verificando vídeos..." + }, + "notFound": { + "badge": "Non atopado", + "detailsButton": "Mostrar detalles", + "homeButton": "Ir ao inicio", + "text": "Buscamos nos nosos proovedores e non puidemos atopar o contido que estás a buscar. Nós, non aloxamos o contido e non temos control sobre o que está dispoñible. Fai click en 'Mostrar detalles' a continuación para obter máis información.", + "title": "Non puidemos atopar eso" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} restante • Finaliza ás {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Por favor, verifica que eres un humán completando o Captcha. Isto é para mantee sudo-flix seguro!", + "error": "Houbo un erro ao verificar a túa humanidade. Por favor, volve a intentalo.", + "title": "Necesitamos verificar que realmente eres un humán.", + "verifyingHumanity": "Verificando a túa humanidade…" + } + }, + "screens": { + "dmca": { + "text": "Benvido/a á páxona de contacto DMA de sudo-flix! Respetamos os dereitos de propiedade intelectual e queremos abordar calqueiro problema de dereitos de autor de maneira más rápida. Se crees que o teu traballo con dereitos de autor está sendo empregado incorrectamente na nosa plataforma, envñia un aviso DMCA detallado ao correo electrónico que se mostra a continuación. Inclue unha descripción do material con dereitos de autor, os seus datos de contacto e unha declaración de boa fé. Estamos comprometidos a resolver estos asuntos o máis rápido posible e agradecemos a túa cooperación para manter a sudo-flix como un lugar que respeta a creatividade e os dereitos de autor.", + "title": "DMCA" + }, + "loadingApp": "Cargando aplicación", + "loadingUser": "Cargando o teu perfil", + "loadingUserError": { + "logout": "Pechar sesión", + "reset": "Reiniciar servidor personalizado", + "text": "Erro ao cargar o teu perfil", + "textWithReset": "Erro ao cargar o teu perfil dende o teu servidor personalizado, queres reiniciar e volver ao servidor por defecto?" + }, + "migration": { + "failed": "Erro ao migrar os teus datos.", + "inProgress": "Porfavor, espera mientras migramos tus datos. Esto no debería llevar mucho." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nome do dispositivo", + "deviceNamePlaceholder": "Teléfono persoal", + "editProfile": "Editar", + "logoutButton": "Pechar sesión" + }, + "actions": { + "delete": { + "button": "Eliminar conta", + "confirmButton": "Eliminar conta", + "confirmDescription": "Estas seguro/a que queres eliminar a túa conta? Todos os datos serán eliminados!", + "confirmTitle": "Estás seguro/a?", + "text": "Esta acción é irreversible. Todos os datos serán eliminados e nada poderá ser recuperado.", + "title": "Eliminar conta" + }, + "title": "Accións" + }, + "devices": { + "deviceNameLabel": "Nome do dispositivo", + "failed": "Erro ao cargar sesións", + "removeDevice": "Quitar", + "title": "Dispositivos" + }, + "profile": { + "finish": "Acabar de editar", + "firstColor": "Cór de perfil un", + "secondColor": "Cór de perfil dous", + "title": "Editar foto de perfil", + "userIcon": "Icono de usuario" + }, + "register": { + "cta": "Empezar", + "text": "Compartir o teu progreso entre dispositivos e mantelos sincronizados.", + "title": "Sincronizar á nube" + }, + "title": "Conta" + }, + "appearance": { + "activeTheme": "Activo", + "themes": { + "blue": "Azul", + "default": "Por defecto", + "gray": "Gris", + "red": "Vermello", + "teal": "Turquesa" + }, + "title": "Apariencia" + }, + "connections": { + "server": { + "description": "Se che gustaría conectar un servidor personalizado de backend para almacenar os teus datos, activa esto e indica a URL. <0>Instruccións.", + "label": "Servidor personalizado", + "urlLabel": "Servidor personalizado URL" + }, + "title": "Conexións", + "workers": { + "addButton": "Añadir novo", + "description": "Para facer que a aplicación funcione, todo o tráfico é organizado en proxies. Activa esta opción se queres empregar os teus propios workers. <0>Instruccións.", + "emptyState": "Non hai workers aínda, engade un abaixo", + "label": "Usar proxy workers personalizados", + "urlLabel": "URLs dos workers", + "urlPlaceholder": "https://" + } + }, + "reset": "Reinicio", + "save": "Gardar", + "sidebar": { + "info": { + "appVersion": "Versión da aplicación", + "backendUrl": "URL do Backend", + "backendVersion": "Versión do Backend", + "hostname": "Nome do Host (Hostname)", + "insecure": "Non seguro", + "notLoggedIn": "Non iniciache sesión", + "secure": "Seguro", + "title": "Información da aplicación", + "unknownVersion": "Descoñecido", + "userId": "ID do usuario" + } + }, + "subtitles": { + "backgroundLabel": "Opacidade do fondo", + "colorLabel": "Cór", + "previewQuote": "Non debo temer. O medo é o asasino da mente.", + "textSizeLabel": "Tamaño da fonte", + "title": "Subtítulos" + }, + "unsaved": "Tes cambios sen gardar" + } +} diff --git a/src/assets/locales/gu.json b/src/assets/locales/gu.json new file mode 100644 index 00000000..5be167a8 --- /dev/null +++ b/src/assets/locales/gu.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "મૂવી-વેબ એક વેબ એપ્લિકેશન છે જે ઇન્ટરનેટ પર સ્ટ્રીમ માટે શોધ કરે છે. ટીમ વધુમાં વધુ કન્ટેન્ટ ઉપભોગ કરવાની દ્રષ્ટિકોણમાં છે.", + "faqTitle": "સામાન્ય પ્રશ્નો", + "q1": { + "body": "મૂવી-વેબ કોઈપણ કન્ટેન્ટ હોસ્ટ કરતું નથી. જ્યારે તમે જોવા માટે કંઈક પર ક્લિક કરો છો, ત્યારે ઈન્ટરનેટ પસંદ કરેલ મીડિયા માટે શોધાય છે (લોડિંગ સ્ક્રીન પર અને 'વિડિયો સ્ત્રોતો' ટૅબમાં તમે જોઈ શકો છો કે તમે કયા સ્રોતનો ઉપયોગ કરી રહ્યાં છો). મીડિયા ક્યારેય મૂવી-વેબ દ્વારા અપલોડ થતું નથી, બધું આ સર્ચિંગ મિકેનિઝમ દ્વારા થાય છે.", + "title": "કન્ટેન્ટ ક્યાંથી આવે છે?" + }, + "q2": { + "body": "શો અથવા મૂવીની વિનંતી કરવી શક્ય નથી, મૂવી-વેબ કોઈપણ કન્ટેન્ટ નું સંચાલન કરતું નથી. બધી કન્ટેન્ટ ઇન્ટરનેટ પરના સ્ત્રોતો દ્વારા જોવામાં આવે છે.", + "title": "હું શો અથવા મૂવીની વિનંતી ક્યાં કરી શકું?" + }, + "q3": { + "body": "અમારા શોધ પરિણામો ધ મૂવી ડેટાબેઝ (TMDB) દ્વારા સંચાલિત છે અને અમારા સ્ત્રોતોમાં ખરેખર કન્ટેન્ટ છે કે કેમ તે ધ્યાનમાં લીધા વિના પ્રદર્શિત થાય છે.", + "title": "શો અથવા મૂવીના શોધ પરિણામો દર્શાવવામાં આવે છે, પરંતુ હું તેને કેટલાક કારણોથી ચલાવી શકતો નથી?" + }, + "title": "મૂવી-વેબ વિશે" + }, + "actions": { + "copied": "કોપી કર્યું", + "copy": "કૉપી" + }, + "auth": { + "createAccount": "હજુ સુધી એકાઉન્ટ નથી? <0>એકાઉન્ટ બનાવો.", + "deviceNameLabel": "ડિવાઇસ નામ", + "deviceNamePlaceholder": "વ્યક્તિગત ફોન", + "generate": { + "description": "તમારો પાસફ્રેઝ તમારા યુઝરનેમ અને પાસવર્ડ તરીકે કાર્ય કરે છે. તેને સુરક્ષિત રાખવાની ખાતરી કરો કારણ કે તમારે તમારા એકાઉન્ટમાં લૉગિન કરવા માટે તેને દાખલ કરવાની જરૂર પડશે", + "next": "મેં મારો પાસફ્રેઝ સેવ કર્યો છે", + "passphraseFrameLabel": "પાસફ્રેઝ", + "title": "તમારો પાસફ્રેઝ" + }, + "hasAccount": "પહેલેથી જ એકાઉન્ટ છે? <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}} થી કનેક્ટ કરી રહ્યાં છો - એકાઉન્ટ બનાવતા પહેલા કૃપા કરીને ખાતરી કરો કે તમે તેના પર વિશ્વાસ કરો છો", + "no": "પાછા જાઓ", + "title": "શું તમે આ સર્વર પર વિશ્વાસ કરો છો?", + "yes": "મને આ સર્વર પર વિશ્વાસ છે" + }, + "verify": { + "description": "કૃપા કરીને તમારો પાસફ્રેઝ એન્ટર કરો, જેથી એકાઉન્ટ બનાવી શકાય", + "invalidData": "ડેટા માન્ય નથી", + "noMatch": "પાસફ્રેઝ મેળ ખાતો નથી", + "passphraseLabel": "તમારો 12-શબ્દનો પાસફ્રેઝ", + "recaptchaFailed": "ReCaptcha માન્યતા નિષ્ફળ", + "register": "ખાતું બનાવો", + "title": "તમારા પાસફ્રેઝની પુષ્ટિ કરો" + } + }, + "errors": { + "badge": "તે તૂટી ગયું", + "details": "ભૂલ વિગતો", + "reloadPage": "પેજને ફરીથી લોડ કરો", + "showError": "ભૂલ વિગતો બતાવો", + "title": "અમને એક ભૂલ આવી!" + }, + "footer": { + "legal": { + "disclaimer": "અસ્વીકરણ", + "disclaimerText": "મૂવી-વેબ કોઈપણ ફાઇલોને હોસ્ટ કરતું નથી, તે ફક્ત 3ર્ડ પાર્ટી સર્વિસોના સાથે લિંક કરે છે. ફાઇલ હોસ્ટ્સ અને પ્રદાતાઓ સાથે કાનૂની મુદ્દાઓ ઉઠાવવા જોઈએ. વિડિયો પ્રદાતાઓ દ્વારા બતાવવામાં આવેલી કોઈપણ મીડિયા ફાઇલો માટે sudo-flix જવાબદાર નથી." + }, + "links": { + "discord": "ડિસ્કોર્ડ", + "dmca": "DMCA", + "github": "ગિટહબ" + }, + "tagline": "આ ઓપન સોર્સ સ્ટ્રીમિંગ એપ વડે તમારા મનપસંદ શો અને મૂવીઝ જુઓ." + }, + "global": { + "name": "મૂવી-વેબ", + "pages": { + "about": "વિશે", + "dmca": "DMCA", + "login": "પ્રવેશ કરો", + "pagetitle": "{{title}} - મૂવી-વેબ", + "register": "નોંધણી કરો", + "settings": "સેટિંગ્સ" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "બુકમાર્ક્સ" + }, + "continueWatching": { + "sectionTitle": "જોવાનું ચાલુ રાખો" + }, + "mediaList": { + "stopEditing": "સંપાદન બંધ કરો" + }, + "search": { + "allResults": "અમારી પાસે એટલું જ છે!", + "failed": "મીડિયા શોધવામાં નિષ્ફળ, ફરી પ્રયાસ કરો!", + "loading": "લોડ થાય છે...", + "noResults": "અમે કંઈપણ શોધી શક્યા નથી!", + "placeholder": "તમે શું જોવા માંગો છો?", + "sectionTitle": "શોધ પરિણામો" + }, + "titles": { + "day": { + "default": "તમે આ બપોરે શું જોવા માંગો છો?", + "extra": ["સાહસિક લાગે છે? જુરાસિક પાર્ક યોગ્ય પસંદગી હોઈ શકે છે."] + }, + "morning": { + "default": "તમે આ સવારે શું જોવા માંગો છો?", + "extra": ["હું સાંભળું છું કે Before Sunrise સારું છે"] + }, + "night": { + "default": "તમે આજે રાત્રે શું જોવા માંગો છો?", + "extra": [ + "થાકી ગયા છો? મેં સાંભળ્યું છે કે The Exorcise ફિલ્મ સારી છે." + ] + } + } + }, + "media": { + "episodeDisplay": "સિઝન{{season}} એપિસોડ{{episode}}", + "types": { + "movie": "ફિલ્મ", + "show": "શો" + } + }, + "navigation": { + "banner": { + "offline": "તમારું ઇન્ટરનેટ કનેક્શન તપાસો" + }, + "menu": { + "about": "અમારા વિશે", + "donation": "દાન કરો", + "logout": "લૉગ આઉટ", + "register": "cloud સાથે સમન્વયિત કરો", + "settings": "સેટિંગ્સ", + "support": "આધાર" + } + }, + "notFound": { + "badge": "મળી નથી", + "goHome": "ઘરે પાછા", + "message": "અમે બધે જોયું: ડબ્બાની નીચે, કબાટમાં, પ્રોક્સીની પાછળ, પરંતુ આખરે તમે જે page શોધી રહ્યાં છો તે શોધી શક્યા નહીં.", + "title": "page શોધી શક્યું નથી" + }, + "overlays": { + "close": "બંધ" + }, + "player": { + "back": { + "default": "ઘરે પાછા", + "short": "પાછા" + }, + "casting": { + "enabled": "Device પર કાસ્ટ કરી રહ્યું છે..." + }, + "menus": { + "downloads": { + "disclaimer": "ડાઉનલોડ સીધા પ્રદાતા પાસેથી લેવામાં આવે છે. મૂવી-વેબ ડાઉનલોડ્સ કેવી રીતે પ્રદાન કરવામાં આવે છે તેના પર નિયંત્રણ નથી.", + "downloadPlaylist": "પ્લેલિસ્ટ ડાઉનલોડ કરો", + "downloadSubtitle": "વર્તમાન ઉપશીર્ષક ડાઉનલોડ કરો", + "downloadVideo": "વિડિઓ ડાઉનલોડ કરો", + "hlsDisclaimer": "ડાઉનલોડ સીધા પ્રદાતા પાસેથી લેવામાં આવે છે. મૂવી-વેબ ડાઉનલોડ્સ કેવી રીતે પ્રદાન કરવામાં આવે છે તેના પર નિયંત્રણ નથી. કૃપા કરીને નોંધો કે તમે HLS પ્લેલિસ્ટ ડાઉનલોડ કરી રહ્યાં છો, આ અદ્યતન મલ્ટીમીડિયા સ્ટ્રીમિંગથી પરિચિત વપરાશકર્તાઓ માટે બનાવાયેલ છે.", + "onAndroid": { + "1": "Android પર ડાઉનલોડ કરવા માટે, ડાઉનલોડ બટનને ક્લિક કરો પછી, નવા પૃષ્ઠ પર, વિડિઓ પર ટેપ કરો અને પકડી રાખો, પછી સાચવો પસંદ કરો.", + "shortTitle": "ડાઉનલોડ કરો / Android", + "title": "એન્ડ્રોઇડ પર ડાઉનલોડ કરી રહ્યું છે" + }, + "onIos": { + "1": "iOS પર ડાઉનલોડ કરવા માટે, ડાઉનલોડ બટનને ક્લિક કરો પછી, નવા પૃષ્ઠ પર, પર ક્લિક કરો, પછી ફાઇલોમાં સાચવો .", + "shortTitle": "ડાઉનલોડ કરો / iOS", + "title": "iOS પર ડાઉનલોડ કરી રહ્યું છે" + }, + "onPc": { + "1": "પીસી પર, ડાઉનલોડ બટનને ક્લિક કરો પછી, નવા પૃષ્ઠ પર, વિડિઓ પર જમણું ક્લિક કરો અને Save video as પસંદ કરો", + "shortTitle": "ડાઉનલોડ કરો / PC", + "title": "PC પર ડાઉનલોડ કરી રહ્યું છે" + }, + "title": "ડાઉનલોડ કરો" + }, + "episodes": { + "button": "એપિસોડ્સ", + "emptyState": "આ સિઝનમાં કોઈ એપિસોડ નથી, પછીથી ફરી તપાસો!", + "episodeBadge": "એપિસોડ{{episode}}", + "loadingError": "સીઝન લોડ કરવામાં ભૂલ", + "loadingList": "લોડ થાય છે...", + "loadingTitle": "લોડ થાય છે...", + "unairedEpisodes": "આ સિઝનમાં એક અથવા વધુ એપિસોડ અક્ષમ કરવામાં આવ્યા છે કારણ કે તે હજુ સુધી પ્રસારિત થયા નથી." + }, + "playback": { + "speedLabel": "પ્લેબેક ઝડપ", + "title": "પ્લેબેક સેટિંગ્સ" + }, + "quality": { + "automaticLabel": "આપોઆપ ગુણવત્તા", + "hint": "તમે વિવિધ ગુણવત્તા વિકલ્પો મેળવવા માટે <0>સ્રોત સ્વિચ કરવાનો પ્રયાસ કરી શકો છો.", + "iosNoQuality": "Apple દ્વારા નિર્ધારિત મર્યાદાઓને લીધે, આ સ્ત્રોત માટે ગુણવત્તા પસંદગી iOS પર ઉપલબ્ધ નથી. તમે વિવિધ ગુણવત્તા વિકલ્પો મેળવવા માટે <0>બીજા સ્ત્રોત પર સ્વિચ કરવાનો પ્રયાસ કરી શકો છો.", + "title": "ગુણવત્તા" + }, + "settings": { + "downloadItem": "ડાઉનલોડ કરો", + "enableSubtitles": "સબટાઈટલ સક્ષમ કરો", + "experienceSection": "જોવાનો અનુભવ", + "playbackItem": "પ્લેબેક સેટિંગ્સ", + "qualityItem": "ગુણવત્તા", + "sourceItem": "વિડિઓ સ્ત્રોતો", + "subtitleItem": "ઉપશીર્ષક સેટિંગ્સ", + "videoSection": "વિડિઓ સેટિંગ્સ" + }, + "sources": { + "failed": { + "text": "કોઈપણ વિડિઓઝ શોધવાનો પ્રયાસ કરતી વખતે ભૂલ આવી હતી, કૃપા કરીને કોઈ અલગ સ્રોતનો પ્રયાસ કરો.", + "title": "ઉઝરડા કરવામાં નિષ્ફળ" + }, + "noEmbeds": { + "text": "અમે કોઈપણ એમ્બેડ શોધવામાં અસમર્થ હતા, કૃપા કરીને કોઈ અલગ સ્રોતનો પ્રયાસ કરો.", + "title": "કોઈ એમ્બેડ મળ્યાં નથી" + }, + "noStream": { + "text": "આ સ્રોતમાં આ મૂવી અથવા શો માટે કોઈ સ્ટ્રીમ નથી.", + "title": "કોઈ પ્રવાહ નથી" + }, + "title": "સ્ત્રોતો", + "unknownOption": "અજ્ઞાત" + }, + "subtitles": { + "customChoice": "ફાઇલમાંથી ઉપશીર્ષક પસંદ કરો", + "customizeLabel": "કસ્ટમાઇઝ કરો", + "offChoice": "બંધ", + "settings": { + "backlink": "કસ્ટમ સબટાઈટલ", + "delay": "ઉપશીર્ષક વિલંબ", + "fixCapitals": "મૂડીકરણ ઠીક કરો" + }, + "title": "સબટાઈટલ", + "unknownLanguage": "અજ્ઞાત" + } + }, + "metadata": { + "api": { + "text": "API મેટાડેટા લોડ કરી શકાયું નથી, કૃપા કરીને તમારું ઇન્ટરનેટ કનેક્શન તપાસો.", + "title": "API મેટાડેટા લોડ કરવામાં નિષ્ફળ" + }, + "failed": { + "badge": "નિષ્ફળ", + "homeButton": "ઘર જાઓ", + "text": "TMDB માંથી મીડિયાનો મેટાડેટા લોડ કરી શકાયો નથી. કૃપા કરીને તપાસો કે તમારા ઇન્ટરનેટ કનેક્શન પર TMDB બંધ છે અથવા અવરોધિત છે.", + "title": "મેટાડેટા લોડ કરવામાં નિષ્ફળ" + }, + "notFound": { + "badge": "મળી નથી", + "homeButton": "ઘરે પાછા", + "text": "તમે વિનંતી કરેલ મીડિયા અમે શોધી શક્યા નથી. કાં તો તે દૂર કરવામાં આવ્યું છે અથવા તમે URL સાથે ચેડા કર્યા છે.", + "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}}" + }, + "turnstile": { + "description": "કૃપા કરીને જમણી બાજુએ કૅપ્ચા પૂર્ણ કરીને ચકાસો કે તમે માનવ છો. આ મૂવી-વેબને સુરક્ષિત રાખવા માટે છે!", + "error": "તમારી માનવતા ચકાસવામાં નિષ્ફળ. મહેરબાની કરીને ફરીથી પ્રયત્ન કરો.", + "title": "અમારે ચકાસવાની જરૂર છે કે તમે માનવ છો.", + "verifyingHumanity": "તમારી માનવતા ચકાસવી..." + } + }, + "screens": { + "dmca": { + "text": "મૂવી-વેબના DMCA સંપર્ક પૃષ્ઠ પર આપનું સ્વાગત છે! અમે બૌદ્ધિક સંપદા અધિકારોનો આદર કરીએ છીએ અને કોઈપણ કોપીરાઈટની ચિંતાઓને ઝડપથી ઉકેલવા માંગીએ છીએ. જો તમે માનતા હોવ કે તમારા કૉપિરાઇટ કરેલા કાર્યનો અમારા પ્લેટફોર્મ પર અયોગ્ય ઉપયોગ કરવામાં આવ્યો છે, તો કૃપા કરીને નીચે આપેલા ઇમેઇલ પર વિગતવાર DMCA સૂચના મોકલો. કૃપા કરીને કૉપિરાઇટ કરેલી સામગ્રીનું વર્ણન, તમારી સંપર્ક વિગતો અને સદ્ભાવનાની માન્યતાનું નિવેદન શામેલ કરો. અમે આ બાબતોને તાત્કાલિક ઉકેલવા માટે પ્રતિબદ્ધ છીએ અને મૂવી-વેબને સર્જનાત્મકતા અને કૉપિરાઇટનો આદર કરતી જગ્યા રાખવા માટે તમારા સહકારની પ્રશંસા કરીએ છીએ.", + "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": "cloud સાથે સમન્વયિત કરો" + }, + "title": "એકાઉન્ટ" + }, + "appearance": { + "activeTheme": "સક્રિય", + "themes": { + "blue": "વાદળી", + "default": "ડિફૉલ્ટ", + "gray": "ભૂખરા", + "red": "લાલ", + "teal": "ટીલ" + }, + "title": "દેખાવ" + }, + "connections": { + "server": { + "description": "જો તમે તમારો ડેટા સંગ્રહિત કરવા માટે કસ્ટમ બેકએન્ડ સાથે જોડાવા માંગતા હો, તો આને સક્ષમ કરો અને URL પ્રદાન કરો.", + "label": "કસ્ટમ સર્વર", + "urlLabel": "કસ્ટમ સર્વર URL" + }, + "title": "જોડાણો", + "workers": { + "addButton": "નવો કાર્યકર ઉમેરો", + "description": "એપ્લિકેશન કાર્ય કરવા માટે, તમામ ટ્રાફિકને પ્રોક્સીઓ દ્વારા રૂટ કરવામાં આવે છે. જો તમે તમારા પોતાના કામદારોને લાવવા માંગતા હોવ તો આને સક્ષમ કરો.", + "emptyState": "હજુ સુધી કોઈ કામદારો નથી, નીચે એક ઉમેરો", + "label": "કસ્ટમ પ્રોક્સી કાર્યકરોનો ઉપયોગ કરો", + "urlLabel": "વર્કર URL", + "urlPlaceholder": "https://" + } + }, + "reset": "રીસેટ કરો", + "save": "સાચવો", + "sidebar": { + "info": { + "appVersion": "એપ્લિકેશન સંસ્કરણ", + "backendUrl": "બેકએન્ડ URL", + "backendVersion": "બેકએન્ડ સંસ્કરણ", + "hostname": "હોસ્ટનામ", + "insecure": "અસુરક્ષિત", + "notLoggedIn": "તમે લૉગ ઇન નથી", + "secure": "સુરક્ષિત", + "title": "એપ્લિકેશન માહિતી", + "unknownVersion": "અજ્ઞાત", + "userId": "વપરાશકર્તા ID" + } + }, + "subtitles": { + "backgroundLabel": "પૃષ્ઠભૂમિ અસ્પષ્ટ", + "colorLabel": "રંગ", + "previewQuote": "મારે ડરવું જોઈએ નહીં. ડર એ મનનો હત્યારો છે.", + "textSizeLabel": "ટેક્સ્ટનું કદ", + "title": "સબટાઈટલ" + }, + "unsaved": "તમારી પાસે વણસાચવેલા ફેરફારો છે" + } +} diff --git a/src/assets/locales/he.json b/src/assets/locales/he.json new file mode 100644 index 00000000..9ae189e6 --- /dev/null +++ b/src/assets/locales/he.json @@ -0,0 +1,526 @@ +{ + "about": { + "description": "sudo-flix הוא יישום אינטרנט המחפש באינטרנט אחר זרמים. הצוות שואף לגישה מינימליסטית ברובה לצריכת תוכן.", + "faqTitle": "שאלות נפוצות", + "q1": { + "body": "sudo-flix אינו מארח תוכן כלשהו. כאשר אתה לוחץ על משהו לצפייה, האינטרנט מחפש את המדיה שנבחרה (במסך הטעינה ובכרטיסייה 'מקורות וידאו' תוכל לראות באיזה מקור אתה משתמש). מדיה אף פעם לא מועלת על ידי sudo-flix, הכל מתבצע דרך מנגנון חיפוש זה.", + "title": "מאיפה התוכן?" + }, + "q2": { + "body": "לא ניתן לבקש תוכנית או סרט, sudo-flix לא מנהלת שום תוכן. כל התוכן נצפה דרך מקורות באינטרנט.", + "title": "איפה אני יכול לבקש תוכנית או סרט?" + }, + "q3": { + "body": "תוצאות החיפוש שלנו מופעלות על ידי The Movie Database (TMDB) ומוצגות ללא קשר אם למקורות שלנו יש את התוכן.", + "title": "תוצאות החיפוש מציגות את התוכנית או הסרט, למה אני לא יכול להפעיל אותם?" + }, + "title": "על sudo-flix" + }, + "actions": { + "copied": "הועתק", + "copy": "להעתיק" + }, + "auth": { + "createAccount": "אין לך עדיין חשבון? <0>צור חשבון.", + "deviceNameLabel": "שם המכשיר", + "deviceNamePlaceholder": "מכשיר אישי", + "generate": { + "description": "ביטוי הסיסמה שלך משמש כשם המשתמש והסיסמה שלך. אנא הקפד לשמור אותו בטוח מכיוון שתצטרך להזין אותו כדי להתחבר לחשבון שלך", + "next": "אני שמרתי את משפט הסיסמה שלי", + "passphraseFrameLabel": "ביטוי סיסמה", + "title": "משפט הסיסמה שלך" + }, + "hasAccount": "כבר יש לך חשבון? <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}} - אנא אשר שאתה סומך עליו לפני יצירת חשבון", + "no": "חזור", + "title": "האם אתה סומך על שרת זה?", + "yes": "אני בוטח בשרת זה" + }, + "verify": { + "description": "אנא הזן את משפט הסיסמה שלך מקודם כדי לאשר ששמרת אותו וכדי ליצור את חשבונך", + "invalidData": "הנתונים אינם חוקיים", + "noMatch": "ביטוי הסיסמה אינו תואם", + "passphraseLabel": "ביטוי הסיסמה שלך בעל 12 מילים", + "recaptchaFailed": "אימות ReCaptcha נכשל", + "register": "צור חשבון", + "title": "אשר את ביטוי הסיסמה שלך" + } + }, + "errors": { + "badge": "זה נשבר", + "details": "פרטי שגיאה", + "reloadPage": "טען מחדש את הדף", + "showError": "הצג פרטי שגיאה", + "title": "נתקלנו בשגיאה!" + }, + "footer": { + "legal": { + "disclaimer": "תנית ויתור", + "disclaimerText": "sudo-flix אינו מארח קבצים, הוא רק מקשר לשירותי צד שלישי. יש להתייחס לסוגיות משפטיות עם המארחים והספקים של הקבצים. sudo-flix אינה אחראית לכל קבצי מדיה המוצגים על ידי ספקי הווידאו." + }, + "links": { + "discord": "דיסקורד", + "dmca": "DMCA", + "github": "גיטהאב" + }, + "tagline": "צפה בתוכניות ובסרטים האהובים עליך עם אפליקציית סטרימינג זו בקוד פתוח." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "אודות", + "dmca": "זכויות יוצרים", + "login": "התחבר", + "onboarding": "הכנה", + "pagetitle": "{{title}} - sudo-flix", + "register": "הירשם", + "settings": "הגדרות" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "סימניות" + }, + "continueWatching": { + "sectionTitle": "המשך לצפות" + }, + "mediaList": { + "stopEditing": "הפסק עריכה" + }, + "search": { + "allResults": "זה כל מה שיש לנו!", + "failed": "לא הצלחנו למצוא מדיה, נסה שוב!", + "loading": "טוען...", + "noResults": "לא יכולנו למצוא כלום!", + "placeholder": "במה תרצה לצפות?", + "sectionTitle": "תוצאות חיפוש" + }, + "titles": { + "day": { + "default": "במה תרצה לצפות באחר צהריים זה?", + "extra": ["מרגיש הרפתקני? פארק היורה עשוי להיות הבחירה המושלמת."] + }, + "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": "לא יכולנו למצוא את דף זה" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "בטל", + "confirm": "השתמש בהגדרות ברירת המחדל", + "description": "הגדרת ברירת המחדל אינה כוללת את הזרמים הטובים ביותר ויכולה להיות איטית בצורה בלתי נסבלת.", + "title": "אתה בטוח?" + }, + "extension": { + "back": "חזור", + "explainer": "באמצעות תוסף הדפדפן, אתה יכול לקבל את הזרמים הטובים ביותר שיש לנו להציע. עם התקנה פשוטה בלבד.", + "extensionHelp": "אם התקנת את התוסף אך הוא לא מזוהה. פתח את התוסף דרך תפריט התוספות של הדפדפן שלך ובצע את השלבים שעל המסך.", + "status": { + "disallowed": "התוסף אינו מפועל עבור דף זה", + "disallowedAction": "אפשר תוסף", + "failed": "בקשת הסטטוס נכשלה", + "loading": "מחכה שתתקין את התוסף", + "outdated": "גרסת התוסף ישנה מדי", + "success": "התוסף עובד כמצופה!" + }, + "submit": "המשך", + "title": "נתחיל עם התוסף" + }, + "proxy": { + "back": "חזור", + "explainer": "עם שיטת הפרוקסי, אתה יכול לקבל זרמים באיכות מעולה על ידי יצירת פרוקסי בשירות עצמי.", + "input": { + "errorConnection": "לא ניתן להתחבר לפרוקסי", + "errorInvalidUrl": "כתובת לא תקינה", + "errorNotProxy": "ציפה לפרוקסי אבל קיבל אתר", + "label": "כתובת פרוקסי", + "placeholder": "https://" + }, + "link": "למד כיצד ליצור פרוקסי", + "submit": "שלח פרוקסי", + "title": "בואו ניצור פרוקסי חדש" + }, + "start": { + "explainer": "כדי לקבל את הזרמים הכי טובים שאפשר. תצטרך לבחור באיזו שיטת סטרימינג תרצה להשתמש.", + "options": { + "default": { + "text": "אני לא רוצה זרמים באיכות טובה,<0 /> <1>השתמש בהגדרת ברירת המחדל" + }, + "extension": { + "action": "התקן תוסף", + "description": "התקן תוסף דפדפן וקבל גישה למקורות הטובים ביותר.", + "quality": "איכות מעולה", + "title": "תוסף הדפדפן" + }, + "proxy": { + "action": "הגדר פרוקסי", + "description": "הגדר פרוקסי תוך 5 דקות בלבד וקבל גישה למקורות מעולים.", + "quality": "איכות טובה", + "title": "פרוקסי מותאם אישית" + } + }, + "title": "בוא נעשה לך התקנה עם sudo-flix" + } + }, + "overlays": { + "close": "סגור" + }, + "player": { + "back": { + "default": "חזרה לדף הבית", + "short": "חזור" + }, + "casting": { + "enabled": "משדר למכשיר..." + }, + "menus": { + "downloads": { + "disclaimer": "ההורדות נלקחות ישירות מהספק. ל-sudo-flix אין שליטה על האופן שבו מסופקות ההורדות.", + "downloadPlaylist": "הורד פלייליסט", + "downloadSubtitle": "הורד את הכתוביות הנוכחיות", + "downloadVideo": "הורד וידאו", + "hlsDisclaimer": "ההורדות נלקחות ישירות מהספק. ל-sudo-flix אין שליטה על האופן שבו מסופקות ההורדות.

שים לב שאתה מוריד רשימת השמעה של HLS, לא מומלץ להוריד אם אינך מכיר פורמטים מתקדמים של סטרימינג . נסה מקורות שונים עבור פורמטים שונים.", + "onAndroid": { + "1": "כדי להוריד באנדרואיד, לחץ על כפתור ההורדה ולאחר מכן, בדף החדש, הקש והחזק על הסרטון, ולאחר מכן בחר שמור.", + "shortTitle": "הורדה / אנדרויד", + "title": "הורדה באנדרויד" + }, + "onIos": { + "1": "כדי להוריד ב-iOS, לחץ על כפתור ההורדה ולאחר מכן, בדף החדש, לחץ על ולאחר מכן על שמור לקבצים .", + "shortTitle": "הורדה / iOS", + "title": "מוריד ב-iOS" + }, + "onPc": { + "1": "במחשב, לחץ על כפתור ההורדה ולאחר מכן, בדף החדש, לחץ לחיצה ימנית על הסרטון ובחר שמור סרטון בשם", + "shortTitle": "הורדה / PC", + "title": "הורדה במחשב" + }, + "title": "הורד" + }, + "episodes": { + "button": "פרקים", + "emptyState": "אין פרקים בעונה זו, אנא בדוק שוב מאוחר יותר!", + "episodeBadge": "פ{{episode}}", + "loadingError": "ארע שגיאה בטעינת העונה", + "loadingList": "טוען...", + "loadingTitle": "טוען...", + "unairedEpisodes": "פרק אחד או יותר בעונה זו הושבת מכיוון שהם עדיין לא שודרו." + }, + "playback": { + "speedLabel": "מהירות הניגון", + "title": "הגדרות ניגון" + }, + "quality": { + "automaticLabel": "איכות אוטומטית", + "hint": "אתה יכול לנסות <0>להחליף מקור כדי לקבל אפשרויות איכות שונות.", + "iosNoQuality": "בשל מגבלות שהוגדרו על ידי אפל, בחירת איכות אינה זמינה ב-iOS עבור מקור זה. אתה יכול לנסות <0>לעבור למקור אחר כדי לקבל אפשרויות איכות שונות.", + "title": "איכות" + }, + "settings": { + "downloadItem": "הורד", + "enableSubtitles": "אפשר כתוביות", + "experienceSection": "חווית צפייה", + "playbackItem": "הגדרות ניגון", + "qualityItem": "איכות", + "sourceItem": "מקורות וידאו", + "subtitleItem": "הגדרות כתוביות", + "videoSection": "הגדרות וידאו" + }, + "sources": { + "failed": { + "text": "אירעה שגיאה בעת ניסיון למצוא סרטונים, אנא נסה מקור אחר.", + "title": "לא הצליח לחלץ" + }, + "noEmbeds": { + "text": "לא הצלחנו למצוא שום הטעמות, אנא נסה מקור אחר.", + "title": "לא נמצאו הטמעות" + }, + "noStream": { + "text": "למקור זה אין זרמים עבור הסרט או התוכנית הזו.", + "title": "אין זרם" + }, + "title": "מקורות", + "unknownOption": "לא ידוע" + }, + "subtitles": { + "customChoice": "בחר כתובית מקובץ", + "customizeLabel": "התאם", + "offChoice": "כבוי", + "settings": { + "backlink": "כתוביות מותאמות אישית", + "delay": "עיכוב בכיתוב", + "fixCapitals": "תקן שימוש באותיות גדולות" + }, + "title": "כתוביות", + "unknownLanguage": "לא ידוע" + } + }, + "metadata": { + "api": { + "text": "לא ניתן היה לטעון מטא נתונים של ה-API, אנא בדוק את חיבור האינטרנט שלך.", + "title": "טעינת מטא נתונים של ממשק API נכשלה" + }, + "dmca": { + "badge": "הוסר", + "text": "מדיה זו אינה זמינה יותר עקב הודעת הסרה או תביעה בגין זכויות יוצרים.", + "title": "מדיה הוסרה" + }, + "extensionPermission": { + "badge": "הרשאה חסר", + "button": "השתמש בתוסף", + "text": "יש לך את תוסף הדפדפן, אך אנו זקוקים לאישורך כדי להתחיל להשתמש בתוסף.", + "title": "הגדר את התוסף" + }, + "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}}" + }, + "turnstile": { + "description": "אנא ודא שאתה אנושי על ידי מילוי ה-Captcha בצד ימין. זה כדי לשמור על sudo-flix!", + "error": "נכשל אימות האנושיות שלך. בבקשה נסה שוב.", + "title": "אנחנו צריכים לאמת שאתה אנושי.", + "verifyingHumanity": "מאמת את האנושיות שלך..." + } + }, + "screens": { + "dmca": { + "text": "ברוכה הבאה לדף יצירת קשר DMCA של sudo-flix! אנו מכבדים את זכויות הקניין הרוחני ורוצים לטפל בכל חשש לזכויות יוצרים במהירות. אם אתה סבור שהעבודה שלך המוגנת בזכויות יוצרים נוצלה בצורה לא נכונה בפלטפורמה שלנו, אנא שלח הודעת DMCA מפורטת למייל למטה. אנא כלול תיאור של החומר המוגן בזכויות יוצרים, פרטי ההתקשרות שלך והצהרת תום לב. אנו מחויבים לפתור את העניינים הללו באופן מיידי ומעריכים את שיתוף הפעולה שלך בשמירה על sudo-flix מקום שמכבד יצירתיות וזכויות יוצרים.", + "title": "זכויות יוצרים" + }, + "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": "מראה" + }, + "connections": { + "server": { + "description": "אם תרצה להתחבר ל-backend מותאם אישית כדי לאחסן את הנתונים שלך, הפעל זאת וספק את כתובת האתר. <0>הוראות.", + "label": "שרת אישי", + "urlLabel": "כתובת אתר מותאמת אישית של שרת" + }, + "setup": { + "doSetup": "עשה הגדרה", + "errorStatus": { + "description": "נראה כי פריט אחד או יותר בהגדרה זו זקוקים לתשומת לבך.", + "title": "משהו צריך את תשומת ליבך" + }, + "itemError": "משהו לא בסדר בהגדרה הזו. עבור שוב על ההגדרה כדי לתקן את זה.", + "items": { + "default": "הגדרת ברירת מחדל", + "extension": "תוסף", + "proxy": "פרוקסי מותאם אישית" + }, + "redoSetup": "בצע מחדש את ההגדרה", + "successStatus": { + "description": "כל הדברים נמצאים במקום כדי שתוכל להתחיל לצפות במדיה האהובה עליך.", + "title": "הכל מוכן!" + }, + "unsetStatus": { + "description": "אנא לחץ על הלחצן מימין כדי להתחיל בתהליך ההגדרה.", + "title": "לא עברת את ההגדרה" + } + }, + "title": "התחברויות", + "workers": { + "addButton": "הוסף עובד חדש", + "description": "כדי שהאפליקציה תפעל, כל התעבורה מנותבת דרך פרוקסי. הפעל זאת אם אתה רוצה להביא עובדים משלך. <0>הוראות.", + "emptyState": "עדיין אין עובדים, הוסף אחד למטה", + "label": "השתמש בעובדי פרוקסי מותאמים אישית", + "urlLabel": "כתובות אתרים של עובדים", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "שפת האפליקציה", + "languageDescription": "השפה החלה על האפליקציה כולה.", + "thumbnail": "צור טאמבניילים", + "thumbnailDescription": "לרוב, לסרטונים אין טאמבניילים. אתה יכול להפעיל את ההגדרה הזו כדי ליצור אותם, אבל הם יכולים להפוך את הסרטון שלך לאיטי יותר.", + "thumbnailLabel": "צור טאמבניילים", + "title": "העדפות" + }, + "reset": "איפוס", + "save": "לשמור", + "sidebar": { + "info": { + "appVersion": "גרסת האפליקציה", + "backendUrl": "כתובת אתר אחורי", + "backendVersion": "גרסת Backend", + "hostname": "שם מארח", + "insecure": "לא בטוח", + "notLoggedIn": "אתה לא מחובר", + "secure": "מאובטח", + "title": "מידע על האפליקציה", + "unknownVersion": "לא ידוע", + "userId": "זהות המשתמש" + } + }, + "subtitles": { + "backgroundLabel": "אטימות רקע", + "colorLabel": "צבע", + "previewQuote": "אין נחום, אני לא באמת קיים. אני למעשה, חישוק.", + "textSizeLabel": "גודל הטקסט", + "title": "כתוביות" + }, + "unsaved": "יש לך שינויים שלא נשמרו" + } +} diff --git a/src/assets/locales/hi.json b/src/assets/locales/hi.json new file mode 100644 index 00000000..636ed7b5 --- /dev/null +++ b/src/assets/locales/hi.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "मूवी-वेब एक वेब एप्लिकेशन है जो इंटरनेट पर स्ट्रीम्स की खोज करता है। टीम का लक्ष्य सामग्री के उपभोग के लिए अधिकतर न्यूनतम दृष्टिकोण अपनाना है।", + "faqTitle": "सामान्य प्रश्न", + "q1": { + "body": "मूवी-वेब किसी भी कंटेंट को होस्ट नहीं करता है। जब आप देखने के लिए किसी चीज़ पर क्लिक करते हैं, तो इंटरनेट पर चयनित मीडिया की खोज की जाती है (लोडिंग स्क्रीन पर और 'वीडियो स्रोत' टैब में आप देख सकते हैं कि आप किस स्रोत का उपयोग कर रहे हैं)। मीडिया कभी भी मूवी-वेब द्वारा अपलोड नहीं किया जाता है, सब कुछ इस खोज तंत्र के माध्यम से होता है।", + "title": "फिल्म कहां से आती है?" + }, + "q2": { + "body": "किसी शो या मूवी का अनुरोध करना संभव नहीं है, मूवी-वेब किसी भी फिलम का प्रबंधन नहीं करता है। सभी फिल्में इंटरनेट पर स्रोतों के माध्यम से देखी जाती है।", + "title": "मैं किसी शो या मूवी का अनुरोध कहां कर सकता हूं?" + }, + "q3": { + "body": "हमारे खोज परिणाम मूवी डेटाबेस (टीएमडीबी) द्वारा संचालित होते हैं और इस पर ध्यान दिए बिना प्रदर्शित होते हैं कि हमारे स्रोतों में वास्तव में सामग्री है या नहीं।", + "title": "खोज परिणाम शो या मूवी प्रदर्शित करते हैं, मैं फिलम क्यों नहीं चला पा रहा ?" + }, + "title": "मूवी-वेब के बारे में" + }, + "actions": { + "copied": "कॉपी किया गया हुआ है", + "copy": "कॉपी" + }, + "auth": { + "createAccount": "अब तक कोई खाता नहीं है? <0>खाता बनाएं।", + "deviceNameLabel": "डिवाइस का नाम", + "deviceNamePlaceholder": "निजी फ़ोन", + "generate": { + "description": "आपका पासफ़्रेज़ आपके नाम और पासवर्ड के रूप में कार्य करता है। इसे सुरक्षित रखना सुनिश्चित करें क्योंकि आपको अपने खाते में लॉगिन करने के लिए इसे दर्ज करना होगा", + "next": "मैंने अपना पासफ़्रेज़ सहेज कर लिया है", + "passphraseFrameLabel": "पासफ़्रेज़", + "title": "तुम्हारा पासफ़्रेज़" + }, + "hasAccount": "पहले से एक खाता मौजूद है? <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}} से कनेक्ट हो रहे हैं - खाता बनाने से पहले कृपया पुष्टि करें कि आप इस पर भरोसा करते हैं", + "no": "पीछे जाये", + "title": "क्या आपको इस सर्वर पर भरोसा है?", + "yes": "मुझे इस सर्वर पर भरोसा है" + }, + "verify": { + "description": "यह पुष्टि करने के लिए कि आपने इसे सहेज लिया है और अपना खाता बनाने के लिए कृपया अपना पहले का पासफ़्रेज़ दर्ज करें", + "invalidData": "डेटा मान्य नहीं है", + "noMatch": "पासफ़्रेज़ मेल नहीं खाता", + "passphraseLabel": "आपका 12-शब्दों वाला पासफ़्रेज़", + "recaptchaFailed": "कैप्चा सत्यापन विफल रहा", + "register": "खाता बनाएं", + "title": "अपने पासफ़्रेज़ की पुष्टि करें" + } + }, + "errors": { + "badge": "कुछ टूट गया है शायद", + "details": "गलतियों की जानकारी", + "reloadPage": "पृष्ठ को पुन: लोड करें", + "showError": "गलतियों का विवरण दिखाएँ", + "title": "हमें एक गलति का सामना करना पड़ा!" + }, + "footer": { + "legal": { + "disclaimer": "अस्वीकरण", + "disclaimerText": "मूवी-वेब किसी फ़ाइल को होस्ट नहीं करता है, यह केवल तृतीय पक्ष सेवाओं से लिंक करता है। कानूनी मुद्दों को फ़ाइल होस्ट और प्रदाताओं के साथ उठाया जाना चाहिए। मूवी-वेब वीडियो प्रदाताओं द्वारा दिखाई गई किसी भी मीडिया फ़ाइल के लिए ज़िम्मेदार नहीं है।" + }, + "links": { + "discord": "डीसकॉर्ड", + "dmca": "DMCA", + "github": "गिटहब" + }, + "tagline": "अपने पसंदीदा शो और फिल्में देखें, इस ओपन सोर्स स्ट्रीमिंग ऐप के साथ।" + }, + "global": { + "name": "मूवी-वेब", + "pages": { + "about": "साइट के बारे में", + "dmca": "DMCA", + "login": "लॉग इन", + "pagetitle": "{{title}} - मूवी-वेब", + "register": "नया खाता बनायें", + "settings": "सेटिंग्स" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "बुकमार्क" + }, + "continueWatching": { + "sectionTitle": "देखना जारी रखें" + }, + "mediaList": { + "stopEditing": "एडिट करना बंद करे" + }, + "search": { + "allResults": "बस यही है!", + "failed": "मीडिया ढूंढने में विफल, पुनः प्रयास करें!", + "loading": "लोड हो रहा है..।", + "noResults": "हमें कुछ नहीं मिला!", + "placeholder": "क्या देखना चाहते हो?", + "sectionTitle": "खोज के परिणाम" + }, + "titles": { + "day": { + "default": "आप आज शाम क्या देखना चाहेंगे?", + "extra": [ + "साहसी महसूस कर रहे हैं? जुरासिक पार्क शायद सबसे उत्तम विकल्प हो सकता है।" + ] + }, + "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": "हमने हर जगह देखा: डिब्बे के नीचे, कोठरी में, प्रॉक्सी के पीछे लेकिन अंततः वह पेज नहीं मिला जिसे आप ढूंढ रहे थे।", + "title": "वह पृष्ठ नहीं मिल सका" + }, + "overlays": { + "close": "बंद करना" + }, + "player": { + "back": { + "default": "घर वापिस", + "short": "वापिस" + }, + "casting": { + "enabled": "डिवाइस पर कास्ट किया जा रहा है..।" + }, + "menus": { + "downloads": { + "disclaimer": "डाउनलोड सीधे प्रदाता से लिए जाते हैं। मूवी-वेब का इस पर नियंत्रण नहीं है कि डाउनलोड कैसे प्रदान किए जाते हैं।", + "downloadPlaylist": "प्लेलिस्ट डाउनलोड करें", + "downloadSubtitle": "वर्तमान उपशीर्षक डाउनलोड करें", + "downloadVideo": "वीडियो डाउनलोड करें", + "hlsDisclaimer": "डाउनलोड सीधे प्रदाता से लिए जाते हैं। मूवी-वेब का इस पर नियंत्रण नहीं है कि डाउनलोड कैसे प्रदान किए जाते हैं। कृपया ध्यान दें कि आप एक एचएलएस प्लेलिस्ट डाउनलोड कर रहे हैं, यह उन्नत मल्टीमीडिया स्ट्रीमिंग से परिचित उपयोगकर्ताओं के लिए है।", + "onAndroid": { + "1": "एंड्रॉइड पर डाउनलोड करने के लिए, डाउनलोड बटन पर क्लिक करें, फिर नए पेज पर, वीडियो पर टैप करके रखें, फिर save चुनें।", + "shortTitle": "डाउनलोड / एंड्रॉइड", + "title": "एंड्रॉइड पर डाउनलोड हो रहा है" + }, + "onIos": { + "1": "iOS पर डाउनलोड करने के लिए, डाउनलोड बटन पर क्लिक करें, फिर नए पेज पर पर क्लिक करें, फिर फ़ाइलों में सहेजें पर क्लिक करें।", + "shortTitle": "डाउनलोड / आईओएस", + "title": "आईओएस पर डाउनलोड हो रहा है" + }, + "onPc": { + "1": "पीसी पर, डाउनलोड बटन पर क्लिक करें, फिर नए पेज पर, वीडियो पर राइट क्लिक करें और वीडियो को इस रूप में सेव करें चुनें", + "shortTitle": "डाउनलोड / पीसी", + "title": "पीसी पर डाउनलोड हो रहा है" + }, + "title": "डाउनलोड" + }, + "episodes": { + "button": "एपिसोड", + "emptyState": "इस सीज़न में कोई एपिसोड नहीं है, बाद में दोबारा जाँचें!", + "episodeBadge": "E{{episode}}", + "loadingError": "सीज़न लोड करने में त्रुटि", + "loadingList": "लोड हो रहा है..।", + "loadingTitle": "लोड हो रहा है..।", + "unairedEpisodes": "इस सीज़न में एक या अधिक एपिसोड अक्षम कर दिए गए हैं क्योंकि वे अभी तक प्रसारित नहीं हुए हैं।" + }, + "playback": { + "speedLabel": "प्लेबैक गति", + "title": "प्लेबैक सेटिंग्स" + }, + "quality": { + "automaticLabel": "स्वचालित गुणवत्ता", + "hint": "आप विभिन्न गुणवत्ता विकल्प प्राप्त करने के लिए <0>स्रोत स्विचिंग का प्रयास कर सकते हैं।", + "iosNoQuality": "Apple द्वारा परिभाषित सीमाओं के कारण, इस स्रोत के लिए iOS पर गुणवत्ता चयन उपलब्ध नहीं है। आप विभिन्न गुणवत्ता विकल्प प्राप्त करने के लिए <0>किसी अन्य स्रोत पर स्विच करने का प्रयास कर सकते हैं।", + "title": "गुणवत्ता" + }, + "settings": { + "downloadItem": "डाउनलोड", + "enableSubtitles": "उपशीर्षक सक्षम करें", + "experienceSection": "देखने का अनुभव", + "playbackItem": "प्लेबैक सेटिंग्स", + "qualityItem": "चित्र की गुणवत्ता", + "sourceItem": "वीडियो स्रोत", + "subtitleItem": "उपशीर्षक सेटिंग्स", + "videoSection": "वीडियो सेटिंग्स" + }, + "sources": { + "failed": { + "text": "किसी भी वीडियो को खोजने का प्रयास करते समय एक त्रुटि हुई, कृपया किसी भिन्न स्रोत का प्रयास करें।", + "title": "स्क्रैप करने में विफल" + }, + "noEmbeds": { + "text": "हम कोई एम्बेड नहीं ढूंढ पाए, कृपया कोई भिन्न स्रोत आज़माएँ।", + "title": "कोई एंबेड नहीं मिला" + }, + "noStream": { + "text": "इस स्रोत के पास इस फिल्म या शो के लिए कोई स्ट्रीम नहीं है।", + "title": "कोई धारा नहीं" + }, + "title": "स्रोत", + "unknownOption": "अज्ञात" + }, + "subtitles": { + "customChoice": "फ़ाइल से उपशीर्षक चुनें", + "customizeLabel": "अनुकूलित करें", + "offChoice": "बंद", + "settings": { + "backlink": "कस्टम उपशीर्षक", + "delay": "उपशीर्षक विलंब", + "fixCapitals": "पूंजीकरण ठीक करें" + }, + "title": "उपशीर्षक", + "unknownLanguage": "अज्ञात" + } + }, + "metadata": { + "api": { + "text": "एपीआई मेटाडेटा लोड नहीं हो सका, कृपया अपना इंटरनेट कनेक्शन जांचें।", + "title": "एपीआई मेटाडेटा लोड करने में विफल" + }, + "failed": { + "badge": "असफल", + "homeButton": "घर जाओ", + "text": "टीएमडीबी से मीडिया का मेटाडेटा लोड नहीं किया जा सका। कृपया जांचें कि क्या आपके इंटरनेट कनेक्शन पर टीएमडीबी बंद है या अवरुद्ध है।", + "title": "मेटाडेटा लोड करने में विफल" + }, + "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}}" + }, + "turnstile": { + "description": "कृपया दाईं ओर दिए गए कैप्चा को पूरा करके सत्यापित करें कि आप इंसान हैं। यह मूवी-वेब को सुरक्षित रखने के लिए है!", + "error": "आपकी मानवता को सत्यापित करने में विफल. कृपया पुन: प्रयास करें।", + "title": "हमें यह सत्यापित करने की आवश्यकता है कि आप इंसान हैं।", + "verifyingHumanity": "आपकी मानवता का सत्यापन..।" + } + }, + "screens": { + "dmca": { + "text": "मूवी-वेब के DMCA संपर्क पृष्ठ में आपका स्वागत है! हम बौद्धिक संपदा अधिकारों का सम्मान करते हैं और किसी भी कॉपीराइट संबंधी चिंताओं का शीघ्र समाधान करना चाहते हैं। यदि आपको लगता है कि आपके कॉपीराइट किए गए कार्य का हमारे प्लेटफ़ॉर्म पर अनुचित तरीके से उपयोग किया गया है, तो कृपया नीचे दिए गए ईमेल पर एक विस्तृत DMCA नोटिस भेजें। कृपया कॉपीराइट सामग्री का विवरण, अपना संपर्क विवरण और सद्भावना विश्वास का एक बयान शामिल करें। हम इन मामलों को तुरंत हल करने के लिए प्रतिबद्ध हैं और मूवी-वेब को रचनात्मकता और कॉपीराइट का सम्मान करने वाला स्थान बनाए रखने में आपके सहयोग की सराहना करते हैं।", + "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": "उपस्थिति" + }, + "connections": { + "server": { + "description": "यदि आप अपना डेटा संग्रहीत करने के लिए कस्टम बैकएंड से कनेक्ट करना चाहते हैं, तो इसे सक्षम करें और URL प्रदान करें।", + "label": "कस्टम सर्वर", + "urlLabel": "कस्टम सर्वर यूआरएल" + }, + "title": "सम्बन्ध", + "workers": { + "addButton": "नया कार्यकर्ता जोड़ें", + "description": "एप्लिकेशन को कार्यशील बनाने के लिए, सभी ट्रैफ़िक को प्रॉक्सी के माध्यम से रूट किया जाता है। यदि आप अपने स्वयं के श्रमिकों को लाना चाहते हैं तो इसे सक्षम करें।", + "emptyState": "अभी तक कोई कर्मचारी नहीं, नीचे एक जोड़ें", + "label": "कस्टम प्रॉक्सी कार्यकर्ताओं का उपयोग करें", + "urlLabel": "कार्यकर्ता यूआरएल", + "urlPlaceholder": "https://" + } + }, + "reset": "रीसेट", + "save": "सेव", + "sidebar": { + "info": { + "appVersion": "एप्लिकेशन वेरीज़न", + "backendUrl": "बैकएंड यूआरएल", + "backendVersion": "बैकएंड संस्करण", + "hostname": "होस्ट का नाम", + "insecure": "असुरक्षित", + "notLoggedIn": "आपने लॉग - इन नहीं किया", + "secure": "सुरक्षित", + "title": "ऐप की जानकारी", + "unknownVersion": "अज्ञात", + "userId": "उपयोगकर्ता पहचान" + } + }, + "subtitles": { + "backgroundLabel": "पृष्ठभूमि अस्पष्टता", + "colorLabel": "रंग", + "previewQuote": "मुझे डरना नहीं चाहिए. डर मन हत्यारा है।", + "textSizeLabel": "टेक्स्ट का साइज़", + "title": "उपशीर्षक" + }, + "unsaved": "आपके पास सहेजे नहीं गए परिवर्तन हैं" + } +} diff --git a/src/assets/locales/id.json b/src/assets/locales/id.json new file mode 100644 index 00000000..db53778e --- /dev/null +++ b/src/assets/locales/id.json @@ -0,0 +1,417 @@ +{ + "about": { + "description": "sudo-flix adalah website yang mengumpulkan sumber media streaming dari internet. Didesain khusus dengan pendekatan minimalis agar mempermudah konsumsi konten.", + "faqTitle": "Pertanyaan umum", + "q1": { + "body": "sudo-flix tidak menyimpan berkas media apapun. Ketika anda mengklik sesuatu untuk ditonton, aplikasi akan mencari berkas media di internet (saat media dimuat dan pada tab 'sumber media' anda dapat melihat sumber mana yang digunakan). sudo-flix tidak pernah mengunggah media apapun, semua media didapat melalui mekanisme pencarian di internet.", + "title": "Dari mana konten media di sini berasal?" + }, + "q2": { + "body": "Kami tidak menerima permintaan penambahan serial televisi atau film, sudo-flix tidak mengelola konten apapun. Semua konten ditonton melalui sumber-sumber dari internet.", + "title": "Di mana saya dapat meminta serial televisi atau film untuk ditambahkan?" + }, + "q3": { + "body": "Hasil pencarian aplikasi ini menggunakan API milik The Movie Database (TMDB) dan menampilkan semua hasil pencarian terlepas dari apakah sumber media tersebut dapat ditemukan di penyedia pihak ketiga atau tidak.", + "title": "Hasil pencarian menampilkan serial televisi atau film yang saya cari, tapi mengapa saya tidak bisa memutarnya?" + }, + "title": "Tentang sudo-flix" + }, + "actions": { + "copied": "Disalin", + "copy": "Salin" + }, + "auth": { + "createAccount": "Belum memiliki akun? <0>Buat akun.", + "deviceNameLabel": "Nama perangkat", + "deviceNamePlaceholder": "Perangkat personal", + "generate": { + "description": "Kombinasi kata ini berfungsi sebagai nama pengguna sekaligus kata sandi anda. Pastikan untuk menyimpannya dengan aman karena anda memerlukannya untuk masuk ke akun anda", + "next": "Saya sudah menyimpan kombinasi kata di atas", + "passphraseFrameLabel": "Kombinasi kata", + "title": "Kombinasi kata anda" + }, + "hasAccount": "Sudah memiliki akun? <0>Masuk disini.", + "login": { + "description": "Mohon masukkan kombinasi kata anda untuk masuk ke akun anda", + "deviceLengthError": "Mohon masukkan nama perangkat", + "passphraseLabel": "12 kombinasi kata unik", + "passphrasePlaceholder": "Kombinasi kata", + "submit": "Masuk", + "title": "Masuk ke akun anda", + "validationError": "Kombinasi kata unik salah" + }, + "register": { + "information": { + "color1": "Gradasi warna profil pertama", + "color2": "Gradasi warna profil kedua", + "header": "Masukkan nama perangkat anda lalu pilih warna latar belakang dan ikon pengguna yang ingin anda gunakan", + "icon": "Ikon pengguna", + "next": "Berikutnya", + "title": "Informasi akun" + } + }, + "trust": { + "failed": { + "text": "Apakah anda mengkonfigurasi server dengan benar?", + "title": "Gagal terhubung dengan server" + }, + "host": "Anda ingin terhubung ke <0>{{hostname}} - mohon konfirmasi anda mempercayai server ini sebelum anda membuat akun", + "no": "Kembali", + "title": "Apakah anda mempercayai server ini?", + "yes": "Saya percaya" + }, + "verify": { + "description": "Mohon masukkan kombinasi kata pribadi anda sebelumnya untuk mengonfirmasi bahwa anda telah menyimpannya untuk melanjutkan proses pembuatan akun", + "invalidData": "Data tidak valid", + "noMatch": "Kombinasi kata tidak cocok", + "passphraseLabel": "12 kombinasi kata pribadi anda", + "recaptchaFailed": "Validasi reCaptcha gagal", + "register": "Buat akun", + "title": "Konfirmasi kombinasi kata pribadi anda" + } + }, + "errors": { + "badge": "Terjadi masalah", + "details": "Detail eror", + "reloadPage": "Muat ulang halaman", + "showError": "Lihat detail eror", + "title": "Kami mengalami galat!" + }, + "footer": { + "legal": { + "disclaimer": "Sanggahan", + "disclaimerText": "sudo-flix tidak menyimpan berkas media apapun, aplikasi ini hanya menampilkan tautan dari pihak ketiga. Sengketa hukum harus diajukan langsung kepada penyedia pihak ketiga. sudo-flix tidak bertanggung jawab atas berkas media yang ditampilkan oleh penyedia pihak ketiga." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Tonton serial televisi dan film favorit anda dengan aplikasi streaming open source ini." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Tentang", + "dmca": "DMCA", + "login": "Masuk", + "pagetitle": "{{title}} - sudo-flix", + "register": "Daftar", + "settings": "Pengaturan" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Penanda" + }, + "continueWatching": { + "sectionTitle": "Lanjut menonton" + }, + "mediaList": { + "stopEditing": "Berhenti mengubah" + }, + "search": { + "allResults": "Hanya itu yang kami punya!", + "failed": "Gagal menemukan media, coba lagi!", + "loading": "Memuat...", + "noResults": "Kami tidak dapat menemukan apapun!", + "placeholder": "Apa yang ingin anda tonton?", + "sectionTitle": "Hasil pencarian" + }, + "titles": { + "day": { + "default": "Apa yang ingin anda tonton sore ini?", + "extra": [ + "Lagi pengen nonton genre Adventure? Jurassic Park mungkin cocok buat anda." + ] + }, + "morning": { + "default": "Apa yang ingin anda tonton pagi ini?", + "extra": ["Kayaknya film Before Sunrise bagus deh"] + }, + "night": { + "default": "Apa yang ingin anda tonton malam ini?", + "extra": ["Capek? Katanya The Exocist rekomended."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Serial TV" + } + }, + "navigation": { + "banner": { + "offline": "Periksa koneksi internet anda" + }, + "menu": { + "about": "Tentang kami", + "donation": "Donasi", + "logout": "Keluar", + "register": "Sinkronkan ke cloud", + "settings": "Pengaturan", + "support": "Bantuan" + } + }, + "notFound": { + "badge": "Tidak ditemukan", + "goHome": "Kembali", + "message": "Kami sudah mencari dimana-mana: di bawah tempat sampah, di lemari, di belakang server proxy, tapi tetap gagal menemukan halaman yang anda cari.", + "title": "Gagal menemukan halaman" + }, + "overlays": { + "close": "Tutup" + }, + "player": { + "back": { + "default": "Kembali", + "short": "Kembali" + }, + "casting": { + "enabled": "Casting ke perangkat..." + }, + "menus": { + "downloads": { + "disclaimer": "Tautan unduhan diambil langsung dari penyedia pihak ketiga. Aplikasi ini tidak memiliki kendali bagaimana unduhan disediakan.", + "downloadPlaylist": "Unduh playlist", + "downloadSubtitle": "Unduh subtitle", + "downloadVideo": "Unduh media", + "hlsDisclaimer": "Tautan unduhan diambil langsung dari penyedia pihak ketiga. Aplikasi ini tidak memiliki kendali bagaimana unduhan disediakan. Harap diperhatikan, anda akan mengunduh HLS playlist, media ini hanya ditunjukan bagi pengguna tingkat lanjut.", + "onAndroid": { + "1": "Untuk mengunduh di Android, klik tombol unduh, lalu di halaman baru klik dan tahan pada video, lalu pilih save.", + "shortTitle": "Unduh / Android", + "title": "Unduh di Android" + }, + "onIos": { + "1": "Untuk mengunduh di iOS, klik tombol unduh, lalu di halaman baru, klik , lalu Save to Files .", + "shortTitle": "Unduh / iOS", + "title": "Unduh di iOS" + }, + "onPc": { + "1": "Untuk mengunduh di PC, klik tombol unduh, lalu di halaman baru klik kanan video, dan pilih Save video as", + "shortTitle": "Unduh / PC", + "title": "Unduh di PC" + }, + "title": "Unduh" + }, + "episodes": { + "button": "Episode", + "emptyState": "Tidak ada episode di season ini, check lagi nanti!", + "episodeBadge": "E{{episode}}", + "loadingError": "Eror memuat season", + "loadingList": "Memuat...", + "loadingTitle": "Memuat..." + }, + "playback": { + "speedLabel": "Kecepatan pemutar", + "title": "Pengaturan pemutar" + }, + "quality": { + "automaticLabel": "Otomatis", + "hint": "Anda dapat mencoba <0>mengganti sumber media untuk mendapatkan opsi kualitas yang berbeda.", + "iosNoQuality": "Karena keterbatasan dari Apple, opsi kualitas pada sumber ini tidak tersedia untuk iOS. Anda dapat mencoba <0>mengganti sumber media untuk mendapatkan opsi kualitas yang berbeda.", + "title": "Kualitas" + }, + "settings": { + "downloadItem": "Unduh", + "enableSubtitles": "Hidupkan subtitle", + "experienceSection": "Pengaturan tambahan", + "playbackItem": "Pengaturan pemutar", + "qualityItem": "Kualitas", + "sourceItem": "Sumber media", + "subtitleItem": "Pengaturan subtitle", + "videoSection": "Pengaturan media" + }, + "sources": { + "failed": { + "text": "Terjadi galat saat mencoba mencari media, mohon pilih sumber yang lain.", + "title": "Gagal memuat data" + }, + "noEmbeds": { + "text": "Kami tidak dapat menemukan tautan, mohon pilih sumber yang lain.", + "title": "Tautan tidak ditemukan" + }, + "noStream": { + "text": "Sumber ini tidak memiliki media untuk film atau seri yang anda cari.", + "title": "Tidak ada media" + }, + "title": "Sumber", + "unknownOption": "Tidak diketahui" + }, + "subtitles": { + "customChoice": "Pilih subtitle dari file", + "customizeLabel": "Sesuaikan", + "offChoice": "Matikan", + "settings": { + "backlink": "Subtitle khusus", + "delay": "Penundaan subtitle", + "fixCapitals": "Perbaiki kapitalisasi" + }, + "title": "Subtitle", + "unknownLanguage": "Tidak diketahui" + } + }, + "metadata": { + "failed": { + "badge": "Gagal", + "homeButton": "Kembali", + "text": "Tidak dapat memuat metadata dari TMDB. Mohon periksa apakah TMDB sedang nonaktif atau diblokir pada koneksi internet anda.", + "title": "Gagal memuat metadata" + }, + "notFound": { + "badge": "Tidak ditemukan", + "homeButton": "Kembali", + "text": "Kami tidak dapat menemukan media yang anda cari. Media mungkin sudah dihapus atau URL yang anda masukan salah.", + "title": "Tidak dapat menemukan media." + } + }, + "nextEpisode": { + "cancel": "Batal", + "next": "Episode selanjutnya" + }, + "playbackError": { + "badge": "Galat pemutar", + "errors": { + "errorAborted": "Proses pemuatan media dibatalkan oleh permintaan penguna.", + "errorDecode": "Meskipun sebelumnya media diputuskan dapat digunakan, terjadi masalah saat mencoba memutar berkas media, yang mengakibatkan terjadinya eror.", + "errorGenericMedia": "Terjadi masalah yang tidak diketahui pada media.", + "errorNetwork": "Terjadi kesalahan pada jaringan yang menyebabkan media gagal dimuat, meskipun sebelumnya berhasil.", + "errorNotSupported": "Berkas media atau objek penyedia media tidak didukung." + }, + "homeButton": "Kembali", + "text": "Terjadi masalah saat mencoba memutar media. Mohon coba lagi.", + "title": "Gagal memutar media!" + }, + "scraping": { + "items": { + "failure": "Terjadi masalah", + "notFound": "Media tidak ditemukan", + "pending": "Mencari media..." + }, + "notFound": { + "badge": "Tidak ditemukan", + "detailsButton": "Lihat detail", + "homeButton": "Kembali", + "text": "Kami sudah mencari ke semua penyedia yang kami miliki dan gagal menemukan media yang anda cari! Kami tidak menyimpan berkas media dan tidak memiliki kontrol atas apa yang tersedia. Periksa tombol 'Lihat detail' dibawah ini untuk informasi lebih lanjut.", + "title": "Kami tidak dapat menemukannya" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} tersisa • Selesai pada pukul {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + } + }, + "screens": { + "dmca": { + "text": "Selamat datang di halaman kontak DMCA sudo-flix! Kami menghormati hak kekayaan intelektual dan ingin menangani masalah hak cipta dengan cepat. Jika anda yakin bahwa karya berhak cipta anda telah digunakan secara tidak benar di platform kami, silakan kirim pemberitahuan DMCA secara terperinci ke email di bawah ini. Mohon sertakan deskripsi materi berhak cipta, detail kontak anda, dan pernyataan itikad baik. Kami berkomitmen untuk menyelesaikan masalah ini sesegera mungkin dan kami menghargai kerja sama anda dalam menjaga sudo-flix sebagai tempat yang menghormati kreativitas dan hak cipta.", + "title": "DMCA" + }, + "loadingApp": "Memuat aplikasi", + "loadingUser": "Memuat profil anda", + "loadingUserError": { + "logout": "Keluar", + "reset": "Reset server khusus", + "text": "Gagal memuat profil anda", + "textWithReset": "Gagal memuat profil anda dari server khusus yang anda pilih, ingin kembali ke server default?" + }, + "migration": { + "failed": "Gagal memigrasi data anda.", + "inProgress": "Mohon tunggu, kami sedang memigrasi data anda. Ini tidak akan memakan waktu lama." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nama perangkat", + "deviceNamePlaceholder": "Ponsel personal", + "editProfile": "Ubah", + "logoutButton": "Keluar" + }, + "actions": { + "delete": { + "button": "Hapus akun", + "confirmButton": "Hapus akun", + "confirmDescription": "Apakah anda yakin ingin menghapus akun anda? Seluruh data anda akan hilang!", + "confirmTitle": "Apakah anda yakin?", + "text": "Tindakan ini tidak dapat dibatalkan. Seluruh data akan dihapus dan tidak dapat dipulihkan.", + "title": "Hapus akun" + }, + "title": "Tindakan" + }, + "devices": { + "deviceNameLabel": "Nama perangkat", + "failed": "Gagal memuat sesi", + "removeDevice": "Hapus", + "title": "Perangkat" + }, + "profile": { + "finish": "Selesai mengubah", + "firstColor": "Gradasi warna profil pertama", + "secondColor": "Gradasi warna profil kedua", + "title": "Ubah foto profil", + "userIcon": "Ikon pengguna" + }, + "register": { + "cta": "Memulai", + "text": "Bagikan dan sinkronkan antar perangkat progres tontonan anda.", + "title": "Sinkronkan ke cloud" + }, + "title": "Akun" + }, + "appearance": { + "activeTheme": "Aktif", + "themes": { + "blue": "Biru", + "default": "Default", + "gray": "Abu", + "red": "Merah", + "teal": "Hijau" + }, + "title": "Tampilan" + }, + "connections": { + "server": { + "description": "Jika anda ingin menghubungkan dengan backend khusus untuk menyimpan data, hidupkan ini dan masukkan URL-nya.", + "label": "Server khusus", + "urlLabel": "URL server khusus" + }, + "title": "Koneksi", + "workers": { + "addButton": "Tambah worker baru", + "description": "Agar aplikasi dapat bekerja, seluruh koneksi dialihkan menggunakan proxy. Hidupkan ini jika anda ingin menggunakan worker khusus.", + "emptyState": "Belum ada worker, tambahkan dibawah", + "label": "Gunakan proxy worker khusus", + "urlLabel": "URL worker", + "urlPlaceholder": "https://" + } + }, + "reset": "Reset", + "save": "Simpan", + "sidebar": { + "info": { + "appVersion": "Versi aplikasi", + "backendUrl": "URL Backend", + "backendVersion": "Versi Backend", + "hostname": "Nama host", + "insecure": "Tidak aman", + "notLoggedIn": "Anda belum masuk ke akun anda", + "secure": "Aman", + "title": "Informasi aplikasi", + "unknownVersion": "Tidak diketahui", + "userId": "ID pengguna" + } + }, + "subtitles": { + "backgroundLabel": "Transparansi latar belakang", + "colorLabel": "Warna teks", + "previewQuote": "Jangan takut. Ketakutan adalah pembunuh pikiran.", + "textSizeLabel": "Ukuran teks", + "title": "Subtitle" + }, + "unsaved": "Anda memiliki perubahan yang belum disimpan" + } +} diff --git a/src/assets/locales/is-IS.json b/src/assets/locales/is-IS.json new file mode 100644 index 00000000..0bd89354 --- /dev/null +++ b/src/assets/locales/is-IS.json @@ -0,0 +1,305 @@ +{ + "about": { + "description": "sudo-flix er vefforrit sem leitar á netinu að straumum. Markmið liðsins er naumhyggju nálgun á að horfa á efni.", + "faqTitle": "Algengar spurningar", + "q1": { + "body": "sudo-flix hýsir ekki neitt efni. Þegar þú ýtir á eitthvað til að horfa á, leitað er á netinu fyrir það efni (Þú getur séð hvaða heimild við erum að nota á hleðslu skjánum og í 'myndbands heimildir' flipanum). Skrár eru aldrei settar in af sudo-flix, allt er í gegnum leitarvél.", + "title": "Hvaðan kemur efnið?" + }, + "q2": { + "body": "Þú getur ekki beðið um þætti eða myndir, sudo-flix stýrir ekki neinu efni. Allt efni er skoðað í gegnum heimildir á netinu.", + "title": "Hvar get ég beðið um þætti eða myndir?" + }, + "q3": { + "body": "Okkar leitar niðurstöður eru knúnar af The Movie Database (TMDB) og eru sýndar þótt að okkar heimildir hafa ekki efnið.", + "title": "Leitarniðurstöðurnar sýna þættina eða myndina, af hverju get ég ekki spilað það?" + }, + "title": "Um sudo-flix" + }, + "actions": { + "copied": "Afritað", + "copy": "Afrita" + }, + "auth": { + "createAccount": "Hefurðu ekki reikning ennþá? <0>Búðu til reikning.", + "deviceNameLabel": "Nafn tækis", + "deviceNamePlaceholder": "Einkasími", + "generate": { + "description": "Þitt lykilorð lætur eins og notandanafn og lykilorð. Vertu viss um að halda því öruggu út að þú munt þurfa að nota það til að komast inn í reikningin þinn", + "next": "Ég hef vistað lykilorðið mitt", + "passphraseFrameLabel": "Lykilorð", + "title": "Þitt lykilorð" + }, + "hasAccount": "Hefurðu reikning nú þegar? <0>Skráðu þig inn hér.", + "login": { + "description": "Vinsamlegast sláðu inn lykilorðið þitt til þess að skrá þig inn í reikningin þinn", + "deviceLengthError": "Vinsamlegast settu inn nafn fyrir tækið þitt", + "passphraseLabel": "12-Orða lykilorð", + "passphrasePlaceholder": "Lykilorð", + "submit": "Skrá inn", + "title": "Skráðu þig inn í reikningin þinn", + "validationError": "Vitlaust eða óklárað lykilorð" + }, + "register": { + "information": { + "color1": "Prófíl litur eitt", + "color2": "Prófíl litur tvö", + "header": "Sláðu inn nafn fyrir tækið þitt og veldu liti og notanda tákn að þínu vali", + "icon": "Notanda tákn", + "next": "Næst", + "title": "Reiknings upplýsingar" + } + }, + "trust": { + "failed": { + "text": "Stilltirðu það rétt?", + "title": "Náðist ekki í netþjón" + }, + "host": "Þú ert að tengjast við <0>{{hostname}} - vinsamlegast staðfestu að þú treystir því áður en þú býrð til reikning", + "no": "Fara til baka", + "title": "Treystir þú þessum netþjóni?", + "yes": "Ég treysti þessum netþjóni" + }, + "verify": { + "description": "Vinsamlegst sláðu inn lykilorðið þitt frá því áðan til þess að staðfesta að þú hefur vistað það og til að stofna nýjan reikning", + "invalidData": "Gögn eru ekki gild", + "noMatch": "Lykilorð passar ekki saman", + "passphraseLabel": "Þitt 12-orða lykilorð", + "recaptchaFailed": "ReCaptcha staðfesting mistókst", + "register": "Stofna reikning", + "title": "Staðfestu þitt lykilorð" + } + }, + "errors": { + "badge": "Það bilaði", + "details": "Villu upplýsingar", + "reloadPage": "Endurhlaðaðu vefsíðuna", + "showError": "Sýna villu upplýsingar", + "title": "Við lentum í villu!" + }, + "footer": { + "legal": { + "disclaimer": "Fyrirvari", + "disclaimerText": "sudo-flix hýsir engar skrár, það tengist eingöngu þjónustu þriðja aðila. Lagleg atriði ættu að vera rætt við skráarhýsinga og veitanda. sudo-flix er ekki ábyrg fyrir neinum skrám sýndar af myndbands veitöndum." + }, + "tagline": "Horfðu á uppáhalds þætti og myndirnar þínar með þessu opna hugbúnaða forriti." + }, + "global": { + "pages": { + "about": "Um", + "login": "Skrá inn", + "onboarding": "Setja upp", + "register": "Skrá", + "settings": "Stillingar" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Bókamerki" + }, + "continueWatching": { + "sectionTitle": "Haltu Áfram Að Horfa" + }, + "mediaList": { + "stopEditing": "Hætta að breyta" + }, + "search": { + "allResults": "Þetta er allt sem við höfum!", + "failed": "Mostókst að finna miðil, reyndu aftur!", + "loading": "Hlaðið...", + "noResults": "Við gátum ekki fundið neitt!", + "placeholder": "Hvað viltu horfa á?", + "sectionTitle": "Leitar niðurstöður" + }, + "titles": { + "day": { + "default": "Hvað myndirðu vilja horfa á þetta kvöldið?", + "extra": [ + "Fynnst þér þú vera ævintýralegur? Jurrasic Park gæti verið fullkomna valið." + ] + }, + "morning": { + "default": "Hvað myndirðu vilja horfa á þessum morgni?", + "extra": ["Ég heyrði að Before Sunrise sé góð"] + }, + "night": { + "default": "Hvað myndirðu vilja horfa á í nótt?", + "extra": ["Þreytt? Ég heyrði að The Exorcist sé góð."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} Þ{{episode}}", + "types": { + "movie": "Mynd", + "show": "Þáttur" + } + }, + "navigation": { + "banner": { + "offline": "Athugaðu net tengingu þína" + }, + "menu": { + "about": "Um okkur", + "donation": "Gefa", + "logout": "Skrá út", + "register": "Samstilla við ský", + "settings": "Stillingar", + "support": "Stuðningur" + } + }, + "notFound": { + "badge": "Ekki fundið", + "goHome": "Aftur heim", + "message": "Við leituðum alls staðar: undir ruslafötunum, inn í skápnum, á bakvið proxy þjónin en við gátum ekki fundið síðuna sem þú ert að leita af.", + "title": "Gat ekki fundið síðu" + }, + "overlays": { + "close": "Loka" + }, + "player": { + "back": { + "default": "Aftur heim", + "short": "Til baka" + }, + "casting": { + "enabled": "Steypandi í tæki..." + }, + "menus": { + "downloads": { + "disclaimer": "Niðurhalningar eru teknar beint frá heimildini. sudo-flix hefur engan kraft yfir hvernig niðurhalningarnar eru gefnar.", + "downloadPlaylist": "Hlaða niður spilarlista", + "downloadSubtitle": "Hlaða niður nú verandi texta", + "downloadVideo": "Hlaða niður myndbandi", + "hlsDisclaimer": "Niðurhalningar eru teknar beint frá heimildini. sudo-flix hefur engan kraft yfir hvernig niðurhalningarnar eru gefnar.

Vinsamlegast fattaðu að þú ert að niðurhala HLS spilunarlista, Það erekki mælt með að niðurhala ef þú kannt ekki mikið á háþróað streymis sniði.. Reyndu aðrar heimildir fyrir önnur sniði.", + "onAndroid": { + "shortTitle": "Hlaða niður / Android", + "title": "Að hlaða niður á Android" + }, + "onIos": { + "shortTitle": "Hlaða niður / iOS", + "title": "Að hlaða niður á iOS" + }, + "onPc": { + "shortTitle": "Hlaða niður / PC", + "title": "Að hlaða niður á PC" + }, + "title": "Hlaða niður" + }, + "episodes": { + "button": "Þættir", + "emptyState": "Það eru engir þættir í þessari seríu, kíktu aftur seinna!", + "episodeBadge": "Þ{{episode}}", + "loadingError": "Villa að hlaða seríu", + "loadingList": "Hleðsla...", + "loadingTitle": "Hleðsla...", + "unairedEpisodes": "Einn eða fleiri þættir í þessari seríu eru ekki komnir, út að þeir hafa ekki komið út." + }, + "playback": { + "speedLabel": "Spilunarhraði", + "title": "Spilunar stillingar" + }, + "quality": { + "automaticLabel": "Sjálfkrafa gæði", + "title": "Gæði" + }, + "settings": { + "downloadItem": "Niðurhala", + "enableSubtitles": "Virkja texta", + "experienceSection": "Útsýnis upplifun", + "playbackItem": "Spilunar stillingar", + "qualityItem": "Gæði", + "sourceItem": "Myndbands heimildir", + "subtitleItem": "Texta stillingar", + "videoSection": "Myndbands stillingar" + }, + "sources": { + "failed": { + "text": "Það kom upp villa á meðan við vorum að reyna að finna myndbönd, vinsamlegast reyndu að nota aðra heimild.", + "title": "Skröpun mistókst" + }, + "noEmbeds": { + "text": "Við gátum ekki fundið neina innfellu, vinsamlegast reyndu að nota aðra heimild.", + "title": "Engar innifellur fundnar" + }, + "noStream": { + "text": "Þessi heimild hefur engin streymi fyrir þessa mynd eða þætti.", + "title": "Ekkert streymi" + }, + "title": "Heimildir", + "unknownOption": "Óvitað" + }, + "subtitles": { + "customChoice": "Veldu texta frá skrá", + "customizeLabel": "Sérsníða", + "offChoice": "Af", + "settings": { + "backlink": "Sérsniðir textar", + "delay": "Texta seinkun", + "fixCapitals": "Lagaðu hástafi" + }, + "title": "Textar", + "unknownLanguage": "Óvitað" + } + }, + "metadata": { + "dmca": { + "badge": "Fjarlæga", + "title": "Miðill hefur verið fjarlægður" + }, + "extensionPermission": { + "badge": "Leyfi vantar", + "button": "Nota viðbótina", + "title": "Stilltu viðbótina" + }, + "failed": { + "badge": "Mistókst", + "homeButton": "Fara heim" + }, + "notFound": { + "badge": "Ekki fundið", + "homeButton": "Aftur heim", + "title": "Gat ekki fundið miðil." + } + }, + "nextEpisode": { + "cancel": "Hætta við", + "next": "Næsti þáttur" + }, + "playbackError": { + "badge": "Spilunar villa", + "errors": { + "errorGenericMedia": "Óvituð miðla villa átti sér stað." + }, + "homeButton": "Fara heim", + "title": "Spilun myndbands mistóksts!" + }, + "scraping": { + "items": { + "failure": "Villa kom upp", + "notFound": "Hefur ekki myndbandið", + "pending": "Leitandi af myndböndum..." + }, + "notFound": { + "badge": "Ekki fundið", + "detailsButton": "Sýna smáatriði", + "homeButton": "Fara heim", + "title": "Við gátum ekki fundið þetta" + } + }, + "turnstile": { + "description": "Vinsamlegast sannreyndu að þú sért manneskja með því að klára Captcha-ið til hægri. Þetta er til þess að halda sudo-flix öruggu!", + "error": "Mistókst að sannreyna að þú sért manneskja. Vinsamlegast reyndu aftur.", + "title": "Við þurfum að sannræna að þú ert manneskja.", + "verifyingHumanity": "Að sannræna að þú sért manneskja..." + } + }, + "screens": { + "loadingApp": "Hlaðandi forriti", + "loadingUser": "Hlaðandi þínum reikningi", + "loadingUserError": { + "logout": "Skrá út", + "reset": "Endurstilla sérsniðin netþjón" + } + } +} diff --git a/src/assets/locales/it.json b/src/assets/locales/it.json new file mode 100644 index 00000000..869d50bb --- /dev/null +++ b/src/assets/locales/it.json @@ -0,0 +1,533 @@ +{ + "about": { + "description": "sudo-flix è un'applicazione web che cerca per gli stream sull'internet. Il team cerca di usare un approccio per lo più minimalista per usufruire del contenuto.", + "faqTitle": "Domande comuni", + "q1": { + "body": "sudo-flix non ospita alcun contento. Quando clicci su qualcosa da guardare, sudo-flix cerca l'internet per media di vostra scelta (Sull schermata di caricamento e nell tab 'sorgenti video' si può vedere quale sorgente si sta utilizzando). I media non sono mai caricati da sudo-flix, tutto è attraverso questo meccanismo di ricerca.", + "title": "Da dove arriva il contenuto?" + }, + "q2": { + "body": "Non è possibile richiedere uno show o un film, sudo-flix non gestisce alcun contenuto. Tutti i contenuti sono visualizzati da sorgenti online.", + "title": "Dove posso richiedere una serie o un film?" + }, + "q3": { + "body": "Nostri risultati sono alimentati da «The Movie Database (TMDB)» e visualizzare indipendentemente dal fatto che nostri sorgenti hanno effettivamente il contenuto.", + "title": "I risultati di ricerca mostrano la serie o il film, perché non posso riprodurlo?" + }, + "title": "Su di sudo-flix" + }, + "actions": { + "copied": "Copiato", + "copy": "Copia" + }, + "auth": { + "createAccount": "Non hai ancora un account? <0>Crea un account.", + "deviceNameLabel": "Nome del dispositivo", + "deviceNamePlaceholder": "Telefono personale", + "generate": { + "description": "La tua frase password agisce come il tuo nome utente e password. Assicurati di tenerla al sicuro dato che ti servirà per accedere al tuo account", + "next": "Ho salvato il mio frase password", + "passphraseFrameLabel": "Frase password", + "title": "La tua frase password" + }, + "hasAccount": "Hai già un account? <0>Accedi ", + "login": { + "description": "Inserisci la tua frase password per accedere al proprio account", + "deviceLengthError": "Inserisci un nome per il dispositivo", + "passphraseLabel": "Frase password di 12 parole", + "passphrasePlaceholder": "Frase password", + "submit": "Accedi", + "title": "Accedi al proprio account", + "validationError": "Frase password incompleta o sbagliata" + }, + "register": { + "information": { + "color1": "Colore profilo uno", + "color2": "Colore profilo due", + "header": "Inserisci un nome per il tuo dispositivo e seleziona, un colore e un'icona utente a tua scelta", + "icon": "Icona utente", + "next": "Avanti", + "title": "Informazioni sull'account" + } + }, + "trust": { + "failed": { + "text": "L'avete configurata correttamente?", + "title": "Impossibile raggiungere il server" + }, + "host": "Ti stai collegando a <0>{{hostname}} - conferma la tua fiducia prima di creare un account", + "no": "Indietro", + "title": "Ti fidi di questo server?", + "yes": "Mi fido di questo server" + }, + "verify": { + "description": "Inserisci il tuo frase password da prima per confermare che è salvato per creare il proprio account", + "invalidData": "I dati non sono validi", + "noMatch": "Frase password non corrisponde", + "passphraseLabel": "La propria frase password di 12 parole", + "recaptchaFailed": "Validazione ReCaptcha fallita", + "register": "Crea account", + "title": "Conferma la propria frase password" + } + }, + "errors": { + "badge": "Si è rotto", + "details": "Dettagli errore", + "reloadPage": "Ricarica la pagina", + "showError": "Mostra dettagli dell'errore", + "title": "Si è verificato un errore!" + }, + "footer": { + "legal": { + "disclaimer": "Disclaimer", + "disclaimerText": "sudo-flix non ospita qualsiasi file, si collega solo a fonti di terze parti. Se avete problemi legali, rivolgetevi ai file host e ai provider. sudo-flix non è responsabile per i file multimediali mostrati dai video provider." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Guardate i vostri preferiti programmi televisivi e film con questo app di streaming open source." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Su di sudo-flix", + "dmca": "DMCA", + "login": "Accedi", + "onboarding": "Setup", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registrarsi", + "settings": "Impostazioni" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Segnalibri" + }, + "continueWatching": { + "sectionTitle": "Continua a guardare" + }, + "mediaList": { + "stopEditing": "Smettere di modificare" + }, + "search": { + "allResults": "È tutto ciò che abbiamo!", + "failed": "Impossibile trovare i media, riprova!", + "loading": "Caricamento...", + "noResults": "Non abbiamo trovato nulla!", + "placeholder": "Cosa vuoi guardare?", + "sectionTitle": "Risultati della ricerca" + }, + "titles": { + "day": { + "default": "Cosa vorresti guardare questo pomeriggio?", + "extra": [ + "Senti avventuroso? Jurassic Park potrebbe essere la scelta perfetta." + ] + }, + "morning": { + "default": "Cosa vorresti guardare questa mattina?", + "extra": ["Ho sentito che «Prima Dell'alba» è buono"] + }, + "night": { + "default": "Cosa vorresti guardare questa sera?", + "extra": ["Stanco? Ho sentito che L'esorciccio è buono."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Serie" + } + }, + "navigation": { + "banner": { + "offline": "Controlla la tua connessione internet" + }, + "menu": { + "about": "Su di sudo-flix", + "donation": "Donare", + "logout": "Esci", + "register": "Sincronizzare con il cloud", + "settings": "Impostazioni", + "support": "Impostazioni" + } + }, + "notFound": { + "badge": "Non trovato", + "goHome": "Torna alla pagina iniziale", + "message": "Abbiamo cercato ovunque: sotto i bidoni, nell'armadio, dietro il proxy, ma alla fine non siamo riusciti a trovare la pagina che stai cercando.", + "title": "Impossibile trovare quella pagina" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Annulla", + "confirm": "Utilizzare setup predefinita", + "description": "Il setup predefinita non offre i migliori stream e può essere incredibilmente lento.", + "title": "Sei sicuro?" + }, + "extension": { + "back": "Torna indietro", + "explainer": "Utilizzando l'estensione del browser, è possibile ottenere i stream ottimi che abbiamo da offrire. Basta una semplice installazione.", + "explainerIos": "Purtroppo, l'estensione del browser non è supportato su IOS, premere Torna indietro per scegliere un'altra opzione.", + "extensionHelp": "Se avete installato l'estensione ma non viene rilevata. Aprire l'estensione attraverso il menu delle estensioni del browser e seguire i passaggi sullo schermo.", + "linkChrome": "Installa l'estensione su Chrome", + "linkFirefox": "Installa l'estensione su Firefox", + "notDetecting": "Installato su Chrome ma non viene visualizzato? Prova a ricaricare la pagina!", + "notDetectingAction": "Ricarica pagina", + "status": { + "disallowed": "L'estensione non è attivato per questa pagina", + "disallowedAction": "Attiva l'estensione", + "failed": "Richiesta di stato fallita", + "loading": "In attesa di installare l'estensione", + "outdated": "Versione dell'estensione troppo vecchia", + "success": "L'estensione funziona come previsto!" + }, + "submit": "Continua", + "title": "Iniziamo con un'estensione" + }, + "proxy": { + "back": "Torna indietro", + "explainer": "Con il metodo proxy, è possibile ottenere stream di qualità ottima creando un proxy self-service.", + "input": { + "errorConnection": "Impossibile connettere al proxy", + "errorInvalidUrl": "URL non valido", + "errorNotProxy": "Avevamo previsto un proxy, ma abbiamo ottenuto un sito web", + "label": "URL proxy", + "placeholder": "https://" + }, + "link": "Impare a creare un proxy", + "submit": "Invia proxy", + "title": "Creiamo un nuovo proxy" + }, + "start": { + "explainer": "Per ottenere i migliori streaming possibili. È necessario scegliere il metodo di streaming da utilizzare.", + "options": { + "default": { + "text": "Non voglio stream di buona qualità, <0 /> <1>usa setup predefinita" + }, + "extension": { + "action": "Installa l'estensione", + "description": "Installate l'estensione del browser per accedere alle migliori sorgenti.", + "quality": "Migliore qualità", + "title": "Estensione del browser" + }, + "proxy": { + "action": "Setup proxy", + "description": "Configurate un proxy in soli 5 minuti e ottenete l'accesso a ottimi fonti.", + "quality": "Buona qualità", + "title": "Proxy personalizzato" + } + }, + "title": "Configuriamo sudo-flix" + } + }, + "overlays": { + "close": "Chiudi" + }, + "player": { + "back": { + "default": "Torna alla pagina iniziale", + "short": "Indietro" + }, + "casting": { + "enabled": "Casting al dispositivo.." + }, + "menus": { + "downloads": { + "disclaimer": "I download vengono effettuati direttamente dal provider. sudo-flix non ha il controllo sulle modalità di fornitura dei download.", + "downloadPlaylist": "Scarica playlist", + "downloadSubtitle": "Scarica sottotitolo attuale", + "downloadVideo": "Scarica video", + "hlsDisclaimer": "I download vengono effettuati direttamente dal provider. sudo-flix non ha il controllo sul modo in cui i download vengono forniti.

Nota che se state scaricando un playlist HLS, non è raccomandato di scaricare se non si ha familiarità con i formati di streaming avanzati. Prova altri sorgenti per diversi formati.", + "onAndroid": { + "1": "Per scaricare su Android, fai clic sul pulsante di scaricare, e poi nella nuova paggina, toccare e tenere premuto sul video, e selezionare salva.", + "shortTitle": "Scarica / Android", + "title": "Scaricare su Android" + }, + "onIos": { + "1": "Per scaricare su iOS, fai clic sul pulsante di scaricare, e poi nella nuova paggina, fai clic su e poiSalva su FIle .", + "shortTitle": "Scarica / iOS", + "title": "Scaricare su iOS" + }, + "onPc": { + "1": "Sul PC, fare clic sul pulsante di downloa, e poi nella nuova pagina, fai clic destro sul video e selezionare Salva video come", + "shortTitle": "Scarica / PC", + "title": "Scaricare su PC" + }, + "title": "Scarica" + }, + "episodes": { + "button": "Episodi", + "emptyState": "Non ci sono episodi in questa stagione, controlla più tardi!", + "episodeBadge": "E{{episode}}", + "loadingError": "Errore nel caricare la stagione", + "loadingList": "Caricamento...", + "loadingTitle": "Caricamento...", + "unairedEpisodes": "Uno o più episodi di questa stagione sono stati disabilitati perché non sono ancora andati in onda." + }, + "playback": { + "speedLabel": "Velocità di riproduzione", + "title": "Impostazioni di riproduzione" + }, + "quality": { + "automaticLabel": "Qualità automatica", + "hint": "Si può provare <0>cambiare sorgente per prendere diverse opzioni di qualità.", + "iosNoQuality": "A causa delle limitazioni imposti da Apple, la selezione della qualità non è disponibile su iOS per questa sorgente. Si può provare a <0>passare a un'altra sorgente per scegliere opzioni di qualità diverse.", + "title": "Qualità" + }, + "settings": { + "downloadItem": "Scarica", + "enableSubtitles": "Abilita i sottotitoli", + "experienceSection": "Esperienza di visione", + "playbackItem": "Impostazioni di riproduzione", + "qualityItem": "Qualità", + "sourceItem": "Sorgenti video", + "subtitleItem": "Impostazioni sottotitoli", + "videoSection": "Impostazioni video" + }, + "sources": { + "failed": { + "text": "Si è verificato un errore trovare i video, provare con un'altra sorgente.", + "title": "Impossibile raschiare" + }, + "noEmbeds": { + "text": "Non è stato possibile trovare alcun embed, provare con un'altra sorgente.", + "title": "Nessun embed trovato" + }, + "noStream": { + "text": "Questa fonte non ha flussi per questo film o serie.", + "title": "Nessun flusso" + }, + "title": "Sorgenti", + "unknownOption": "Sconosciuto" + }, + "subtitles": { + "customChoice": "Selezionare i sottotitoli dal file", + "customizeLabel": "Personalizzare", + "offChoice": "Spento", + "settings": { + "backlink": "Sottotitoli personalizzati", + "delay": "Ritardo del sottotitolo", + "fixCapitals": "Correggere la capitalizzazione" + }, + "title": "Sottotitoli", + "unknownLanguage": "Sconosciuto" + } + }, + "metadata": { + "api": { + "text": "Impossibile caricare metadati API, verifica la connessione internet.", + "title": "Impossibile caricare metadati API" + }, + "dmca": { + "badge": "Rimosso", + "text": "Questa media non è più disponibile a causa di un notizia takedown o un rivendicazione di copyright.", + "title": "I media sono stati rimossi" + }, + "extensionPermission": { + "badge": "Permesso mancato", + "button": "Usa l'estensione", + "text": "Avete l'estensione del browser, ma serve il vostro permesso per iniziare usare l'estensione.", + "title": "Configura l'estensione" + }, + "failed": { + "badge": "Fallito", + "homeButton": "Vai alla pagina iniziale", + "text": "Impossibile caricare i metadati da TMDB. Verifica che TMDB è offline o bloccato sulla propria connessione Internet.", + "title": "Impossibile caricare i metadati" + }, + "notFound": { + "badge": "Non trovato", + "homeButton": "Torna alla pagina iniziale", + "text": "Non siamo riusciti a trovare il media richiesto. È stato rimosso o hai manomesso l'URL.", + "title": "Impossibile trovare quel media." + } + }, + "nextEpisode": { + "cancel": "Cancella", + "next": "Prossimo episodio" + }, + "playbackError": { + "badge": "Errore di riproduzione", + "errors": { + "errorAborted": "Il recupero del media è stato interrotto dalla richiesta dell'utente.", + "errorDecode": "Nonostante sia stato precedentemente stabilito che è utilizzabile, si è verificato un errore decodifica della risorsa multimediale, con conseguente errore.", + "errorGenericMedia": "Errore multimediale sconosciuto si è verificato.", + "errorNetwork": "Si è verificato un errore di rete che ha impedito il recupero del supporto, nonostante fosse precedentemente disponibile.", + "errorNotSupported": "L'oggetto media o media provider non è supportato." + }, + "homeButton": "Torna a pagina iniziale", + "text": "Si è verificato un errore cercando di riprodurre i media. Riprova.", + "title": "Impossibile riprodurre il video!" + }, + "scraping": { + "items": { + "failure": "Si è verificato un errore", + "notFound": "Non ha il video", + "pending": "Controllo per video..." + }, + "notFound": { + "badge": "Non trovato", + "detailsButton": "Mostra i dettagli", + "homeButton": "Torna a pagina iniziale", + "text": "Abbiamo cercato tra i nostri fornitori e non abbiamo trovato i media che state cercando! Non ospitiamo i media e non abbiamo alcun controllo su ciò che è disponibile. Per maggiori dettagli, fare clic su \"Mostra i dettagli\" qui sotto.", + "title": "Non siamo riusciti a trovare quello" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} rimasto • Finisce a {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Verifica che siate umani completando il Captcha sulla destra. Questo serve a mantenere sudo-flix sicuro!", + "error": "Impossibile verificare il proprio umanità. Riprova.", + "title": "Dobbiamo verificare che lei sia umano.", + "verifyingHumanity": "verificare il proprio umanità..." + } + }, + "screens": { + "dmca": { + "text": "Benvenuti nella pagina di contatto DMCA di sudo-flix! Rispettiamo i diritti di proprietà intellettuale e vogliamo risolvere rapidamente qualsiasi problema di copyright. Se ritenete che il vostro lavoro protetto da copyright sia stato utilizzato in modo improprio sulla nostra piattaforma, inviate una notizia DMCA all'indirizzo e-mail indicato di seguito. Dovresti includere una descrizione del materiale protetto da copyright, i propri dati di contatto e una dichiarazione di buona fede. Ci impegniamo a risolvere tempestivamente questi problemi e apprezziamo la vostra collaborazione per mantenere sudo-flix un luogo che rispetta la creatività e i diritti d'autore.", + "title": "DMCA" + }, + "loadingApp": "Caricamento dell'applicazione", + "loadingUser": "Caricamento del proprio profilo", + "loadingUserError": { + "logout": "Esci", + "reset": "Ripristino del server personalizzato", + "text": "Impossibile caricare il proprio profilo", + "textWithReset": "Impossibile caricare il proprio profile dal server personalizzato, vorresti ripristinare il server predefinito?" + }, + "migration": { + "failed": "Impossible migrare i propri dati.", + "inProgress": "Attendere, stiamo migrando i propri dati." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nome del dispositivo", + "deviceNamePlaceholder": "Telefono personale", + "editProfile": "Modifica", + "logoutButton": "Esci" + }, + "actions": { + "delete": { + "button": "Eliminare l'account", + "confirmButton": "Eliminare l'account", + "confirmDescription": "Sei sicuro che vuoi eliminare il proprio account? Tutti i dati propri andranno persi!", + "confirmTitle": "Sei sicuro?", + "text": "Questa azione è irreversibile. Tutti i propri dati verranno eliminati e non sarà possibile recuperare nulla.", + "title": "Eliminare l'account" + }, + "title": "Azioni" + }, + "devices": { + "deviceNameLabel": "Nome dispositivo", + "failed": "Impossibile caricare sessioni", + "removeDevice": "Rimuovi", + "title": "Dispositivi" + }, + "profile": { + "finish": "Finire di modificare", + "firstColor": "Colore del profilo uno", + "secondColor": "Colore del profilo due", + "title": "Modifica l'immagine del profilo", + "userIcon": "Icona utente" + }, + "register": { + "cta": "Inizia", + "text": "Condividete i progressi di film o serie tra i dispositivi e manteneteli sincronizzati.", + "title": "Sincronizzazione con il cloud" + }, + "title": "Account" + }, + "appearance": { + "activeTheme": "Attivo", + "themes": { + "blue": "Blu", + "default": "Predefinito", + "gray": "Grigio", + "red": "Rosso", + "teal": "Verde acqua" + }, + "title": "Aspetto" + }, + "connections": { + "server": { + "description": "Se si desideri connettersi a un backend personalizzato per memorizzare i dati, attivare questo e fornire l'URL. <0>Istruzioni.", + "label": "Server personalizzato", + "urlLabel": "URL del server personalizzato" + }, + "setup": { + "doSetup": "Fare setup", + "errorStatus": { + "description": "Sembra che uno o più articoli in questo setup serve la vostra attenzione.", + "title": "Qualcosa serve la vostra attenzione" + }, + "itemError": "C'è un errore in questo setup. Eseguire nuovamente il setup per correggerlo.", + "items": { + "default": "Setup predefinita", + "extension": "Estensione", + "proxy": "Proxy personalizzato" + }, + "redoSetup": "Rifare setup", + "successStatus": { + "description": "Tutto è pronto per iniziare a guardare i vostri media preferiti.", + "title": "Tutto è setup!" + }, + "unsetStatus": { + "description": "Fare clic sul pulsante a destra per avviare il setup.", + "title": "Non l'avete completato il setup" + } + }, + "title": "Connessioni", + "workers": { + "addButton": "Aggiungere un nuovo lavoratore", + "description": "Per far funzionare l'applicazione, tutto il traffico viene instradato attraverso i proxy. Abilitare questa opzione se si desidera portare i propri lavoratori. <0>Istruzioni.", + "emptyState": "Non ci sono ancora lavoratori, aggiungetene uno sotto", + "label": "Utilizzare proxy worker personalizzati", + "urlLabel": "URL dei lavoratori", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Lingua di applicazione", + "languageDescription": "Lingua applicato all'intera applicazione.", + "thumbnail": "Generare miniature", + "thumbnailDescription": "Nella maggior parte dei casi, i video non hanno miniature. È possibile attivare questa impostazione per generarle quando è necessario, ma possono rendere il video più lento.", + "thumbnailLabel": "Generare miniature", + "title": "Preferenze" + }, + "reset": "Reset", + "save": "Salva", + "sidebar": { + "info": { + "appVersion": "Versione dell'app", + "backendUrl": "URL backend", + "backendVersion": "Versione backend", + "hostname": "Hostname", + "insecure": "Insicuro", + "notLoggedIn": "Non sei connesso", + "secure": "Sicuro", + "title": "Informazioni dell'app", + "unknownVersion": "Sconosciuto", + "userId": "ID utente" + } + }, + "subtitles": { + "backgroundLabel": "Opacità di sfondo", + "colorLabel": "Colore", + "previewQuote": "Cantami o Diva del pelide Achille l'ira funesta.", + "textSizeLabel": "Dimensione testo", + "title": "Sottotitoli" + }, + "unsaved": "Le modifiche non sono state salvate" + } +} diff --git a/src/assets/locales/ja.json b/src/assets/locales/ja.json new file mode 100644 index 00000000..ec1c9fcc --- /dev/null +++ b/src/assets/locales/ja.json @@ -0,0 +1,109 @@ +{ + "about": { + "description": "sudo-flixは、ウェブアプリケーションで、インターネットを検索してストリームを見つけます。チームは、コンテンツの消費において主にミニマリストなアプローチを目指しています。", + "faqTitle": "一般的な質問", + "q1": { + "body": "sudo-flixはいかなるコンテンツもホストしていません。視聴するために何かをクリックすると、選択したメディアがインターネットで検索されます(ローディング画面と「ビデオソース」タブでは、使用しているソースが表示されます)。メディアは決してsudo-flixにアップロードされず、すべてがこの検索メカニズムを通じて行われます。", + "title": "コンテンツはどこから来ていますか?" + }, + "q2": { + "body": "ショーまたは映画のリクエストはできません。sudo-flixはどのコンテンツも管理していません。すべてのコンテンツはインターネット上のソースを通じて閲覧されます。", + "title": "ショーまたは映画のリクエストはどこでできますか?" + }, + "q3": { + "body": "私たちの検索結果は、The Movie Database(TMDB)によって提供され、実際にはコンテンツを持っているかどうかに関係なく表示されます。", + "title": "検索結果には番組や映画が表示されますが、なぜ再生できないのでしょうか?" + }, + "title": "sudo-flixについて" + }, + "actions": { + "copied": "コピーしました", + "copy": "コピー" + }, + "auth": { + "createAccount": "まだアカウントをお持ちでないですか? <0>アカウントを作成 してください。", + "deviceNameLabel": "デバイス名", + "deviceNamePlaceholder": "個人用の電話", + "generate": { + "description": "パスフレーズはユーザー名とパスワードとして機能します。アカウントにログインする際には必ず入力する必要があるため、安全に保管してください。" + }, + "login": { + "submit": "ログイン", + "title": "自分の口座にログインする" + } + }, + "footer": { + "tagline": "あなたのお気に入りの番組や映画を、このオープンソースのストリーミングアプリで視聴してください。" + }, + "global": { + "name": "映画ウェブ", + "pages": { + "about": "概要", + "login": "ログイン", + "settings": "設定" + } + }, + "home": { + "continueWatching": { + "sectionTitle": "視聴を続ける" + }, + "search": { + "allResults": "それがすべてです!", + "loading": "読み込み中...", + "noResults": "見つかりませんでした!", + "placeholder": "どんな映画を見たい?", + "sectionTitle": "検索結果" + }, + "titles": { + "day": { + "default": "今日の午後はどんな映画を見たい?" + }, + "morning": { + "default": "今朝はどんな映画を見たい?" + }, + "night": { + "default": "今夜はどんな映画を見たい?" + } + } + }, + "media": { + "types": { + "movie": "映画", + "show": "テレビ番組" + } + }, + "navigation": { + "menu": { + "about": "会社概要", + "donation": "寄付", + "logout": "退室", + "register": "クラウドに同期して", + "settings": "設定" + } + }, + "player": { + "menus": { + "episodes": { + "loadingList": "読み込み中...", + "loadingTitle": "読み込み中..." + } + } + }, + "screens": { + "loadingApp": "申込の読み込み", + "loadingUser": "プロフィールの読み込み", + "loadingUserError": { + "logout": "退出" + } + }, + "settings": { + "account": { + "accountDetails": { + "logoutButton": "退室" + }, + "register": { + "title": "クラウドに同期して" + } + } + } +} diff --git a/src/assets/locales/km.json b/src/assets/locales/km.json new file mode 100644 index 00000000..c2928342 --- /dev/null +++ b/src/assets/locales/km.json @@ -0,0 +1,38 @@ +{ + "about": { + "description": "sudo-flix គឺ​ជា​កម្មវិធី​បណ្ដាញវែបសាយ​ដែល​ស្វែងរក​អ៊ីនធឺណិត​សម្រាប់​ការ​ផ្សាយ។ ក្រុមនេះមានគោលបំណងសម្រាប់វិធីសាស្រ្តតិចតួចបំផុតក្នុងការប្រើប្រាស់មាតិកា។", + "faqTitle": "សំណួរទូទៅ", + "q1": { + "body": "sudo-flix មិនផ្ទុកមាតិកាណាមួយទេ។ នៅពេលអ្នកចុចលើអ្វីមួយដើម្បីមើល អ៊ីនធឺណិតត្រូវបានស្វែងរកសម្រាប់មេឌៀដែលបានជ្រើសរើស (នៅលើអេក្រង់ផ្ទុក និងក្នុងផ្ទាំង 'ប្រភពវីដេអូ' អ្នកអាចឃើញប្រភពណាមួយដែលអ្នកកំពុងប្រើ)។ ប្រព័ន្ធផ្សព្វផ្សាយមិនដែលត្រូវបានបង្ហោះដោយគេហទំព័រភាពយន្តនោះទេ អ្វីគ្រប់យ៉ាងគឺតាមរយៈយន្តការស្វែងរកនេះ។", + "title": "តើមាតិកាបានមកពីណា?" + }, + "q2": { + "title": "តើខ្ញុំអាចស្នើសុំកម្មវិធី ឬ ភាពយន្តបាននៅឯណា?" + }, + "q3": { + "body": "លទ្ធផលស្វែងរករបស់យើងត្រូវបានដំណើរការដោយ The Movie Database (TMDB) ហើយបង្ហាញដោយមិនខ្វល់ពីប្រភពរបស់យើងមានខ្លឹមសារឬ​អត់ទេ។", + "title": "លទ្ធផលស្វែងរកបង្ហាញកម្មវិធី ឬ ភាពយន្ត ហេតុអ្វីខ្ញុំមិនអាចមើលបាន?" + }, + "title": "អំពី sudo-flix" + }, + "actions": { + "copied": "បានចម្លង", + "copy": "ចម្លង" + }, + "auth": { + "createAccount": "មិនទាន់មានគណនីមែនទេ? <0>បង្កើតគណនី", + "deviceNameLabel": "ឈ្មោះឧបករណ៍", + "deviceNamePlaceholder": "ទូរស័ព្ទផ្ទាល់ខ្លួន", + "generate": { + "description": "ឃ្លាសម្ងាត់របស់អ្នកដើរតួជាឈ្មោះអ្នកប្រើប្រាស់ និងពាក្យសម្ងាត់របស់អ្នក។ ត្រូវប្រាកដថារក្សាវាឱ្យមានសុវត្ថិភាព ព្រោះអ្នកនឹងត្រូវបញ្ចូលវាដើម្បីចូលគណនីរបស់អ្នក", + "next": "ខ្ញុំបានរក្សាទុកឃ្លាសម្ងាត់របស់ខ្ញុំរួចហើយ", + "passphraseFrameLabel": "ឃ្លាសម្ងាត់", + "title": "ឃ្លាសម្ងាត់របស់អ្នក" + }, + "hasAccount": "មានគណនីរួចហើយ? <0>ចូលទីនេះ", + "login": { + "description": "សូមបញ្ចូលឃ្លាសម្ងាត់របស់អ្នក ដើម្បីចូលគណនីរបស់អ្នក", + "deviceLengthError": "សូមបញ្ចូលឈ្មោះឧបករណ៍" + } + } +} diff --git a/src/assets/locales/ko.json b/src/assets/locales/ko.json new file mode 100644 index 00000000..728f8054 --- /dev/null +++ b/src/assets/locales/ko.json @@ -0,0 +1,402 @@ +{ + "about": { + "description": "sudo-flix은 인터넷에서 스트림을 검색하는 웹 애플리케이션입니다. 저희 팀은 콘텐츠 소비에 대한 최소한의 접근 방식을 지향합니다.", + "faqTitle": "자주 묻는 질문", + "q1": { + "body": "sudo-flix은 어떠한 콘텐츠도 호스팅하지 않습니다. 시청할 콘텐츠를 클릭하면 인터넷에서 선택한 미디어가 검색됩니다(로딩 화면과 '비디오 소스' 탭에서 사용 중인 소스를 확인할 수 있습니다). 무비웹에서 미디어를 업로드하지 않으며, 모든 것이 이 검색 메커니즘을 통해 이루어집니다.", + "title": "콘텐츠의 출처는 어디인가요?" + }, + "q2": { + "body": "프로그램이나 영화를 요청할 수 없으며, sudo-flix은 어떠한 콘텐츠도 관리하지 않습니다. 모든 콘텐츠는 인터넷의 소스를 통해 볼 수 있습니다.", + "title": "프로그램이나 영화는 어디에서 요청할 수 있나요?" + }, + "q3": { + "body": "검색 결과는 영화 데이터베이스(TMDB)를 기반으로 하며, 출처에 실제 콘텐츠가 있는지 여부와 관계없이 표시됩니다.", + "title": "검색 결과에 프로그램이나 영화가 표시되는데 왜 재생할 수 없나요?" + }, + "title": "sudo-flix에 대하여" + }, + "actions": { + "copied": "복사되었습니다", + "copy": "복사하기" + }, + "auth": { + "createAccount": "계정이 없으십니까? <0>계정을 생성하세요.", + "deviceNameLabel": "기기명", + "deviceNamePlaceholder": "개인 휴대폰", + "generate": { + "description": "암호문은 사용자 아이디와 비밀번호 역할을 합니다. 계정에 로그인할 때 발급 받은 암호문을 입력해야 하므로 안전하게 보관하세요", + "next": "암호문을 저장했습니다", + "passphraseFrameLabel": "암호문", + "title": "암호 문구" + }, + "hasAccount": "이미 계정을 보유하고 있으십니까? <0>로그인 하기.", + "login": { + "description": "계정에 로그인하기위해 암호문을 입력하세요", + "deviceLengthError": "기기명을 입력하세요", + "passphraseLabel": "12단어 암호 문구", + "passphrasePlaceholder": "암호 문구", + "submit": "로그인", + "title": "계정에 로그인하기", + "validationError": "틀리거나 잘못된 암호문입니다" + }, + "register": { + "information": { + "color1": "프로필 색상 1", + "color2": "프로필 색상 2", + "header": "기기명을 입력하고 원하는 색상과 사용자 아이콘을 선택하세요", + "icon": "유저 아이콘", + "next": "다음", + "title": "계정 정보" + } + }, + "trust": { + "failed": { + "text": "올바르게 구성하셨습니까?", + "title": "서버 연결에 실패했습니다" + }, + "host": "<0>{{hostname}}에 연결 중입니다 - 계정을 만들기 전에 신뢰하는지 확인해 주세요", + "no": "뒤로 가기", + "title": "이 서버를 신뢰하십니까?", + "yes": "네, 신뢰합니다" + }, + "verify": { + "description": "암호문을 저장했는지 확인하고 계정을 만들려면 앞서 입력한 암호문을 입력하세요", + "invalidData": "데이터가 유효하지 않습니다", + "noMatch": "암호문이 일치하지 않습니다", + "passphraseLabel": "12단어 암호 문구", + "recaptchaFailed": "캡챠인증 실패", + "register": "계정 생성", + "title": "암호문 확인" + } + }, + "errors": { + "badge": "어딘가 고장났습니다", + "details": "에러 상세", + "reloadPage": "페이지 새로고침", + "showError": "에러 상세 보기", + "title": "오류가 발생했습니다!" + }, + "footer": { + "legal": { + "disclaimer": "면책 조항", + "disclaimerText": "sudo-flix은 어떠한 파일도 호스팅하지 않으며, 제3자 서비스에 대한 링크만 제공합니다. 법적 문제는 파일 호스트 및 제공업체와 해결해야 합니다. 동영상 제공업체가 표시하는 미디어 파일에 대해 sudo-flix은 책임을 지지 않습니다." + }, + "tagline": "이 오픈 소스 스트리밍 앱으로 좋아하는 프로그램과 영화를 시청하세요." + }, + "global": { + "pages": { + "about": "소개", + "login": "로그인", + "register": "회원가입", + "settings": "설정" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "북마크" + }, + "continueWatching": { + "sectionTitle": "이어보기" + }, + "mediaList": { + "stopEditing": "수정 중지" + }, + "search": { + "allResults": "이게 전부입니다!", + "failed": "미디어 검색에 실패하였습니다, 다시 시도해주세요!", + "loading": "로딩...", + "noResults": "검색결과가 없습니다!", + "placeholder": "무엇을 보고 싶으신가요?", + "sectionTitle": "검색 결과" + }, + "titles": { + "day": { + "default": "오늘 오후에 무엇을 보고 싶으신가요?", + "extra": [ + "모험을 즐기고 싶으신가요? 쥬라기 공원이 완벽한 선택이 될 수 있습니다." + ] + }, + "morning": { + "default": "오늘 아침에 무엇을 보고 싶으신가요?", + "extra": ["Before Sunrise가 좋다고 들었어요"] + }, + "night": { + "default": "오늘 밤에 무엇을 보고 싶으신가요?", + "extra": ["피곤하신가요? The Exorcist가 좋다고 들었어요."] + } + } + }, + "media": { + "types": { + "movie": "영화", + "show": "쇼" + } + }, + "navigation": { + "banner": { + "offline": "인터넷 연결을 확인해주세요" + }, + "menu": { + "about": "서비스 소개", + "donation": "후원", + "logout": "로그아웃", + "register": "클라우드 동기화", + "settings": "설정", + "support": "지원" + } + }, + "notFound": { + "badge": "찾을 수 없음", + "goHome": "메인으로 돌아가기", + "message": "쓰레기통 아래, 옷장, 프록시 뒤 등 모든 곳을 찾아보았지만 결국 원하는 페이지를 찾을 수 없었습니다.", + "title": "해당 페이지를 찾을 수 없습니다" + }, + "overlays": { + "close": "닫기" + }, + "player": { + "back": { + "default": "메인으로 돌아가기", + "short": "뒤로가기" + }, + "casting": { + "enabled": "장치로 전송 중..." + }, + "menus": { + "downloads": { + "disclaimer": "다운로드는 제공업체에서 직접 가져옵니다. sudo-flix은 다운로드 제공 방식을 통제할 수 없습니다.", + "downloadPlaylist": "플레이리스트 다운로드", + "downloadSubtitle": "현재 자막 다운로드", + "downloadVideo": "영상 다운로드", + "hlsDisclaimer": "다운로드는 제공업체에서 직접 가져옵니다. sudo-flix은 다운로드 제공 방식을 통제할 수 없습니다. 고급 멀티미디어 스트리밍에 익숙한 사용자를 위한 HLS 재생 목록을 다운로드하고 있다는 점에 유의하세요.", + "onAndroid": { + "1": "Android에서 다운로드하려면 다운로드 버튼을 클릭한 다음 새 페이지에서 동영상을 길게 눌러저장을 선택합니다.", + "shortTitle": "다운로드 / Android", + "title": "Android에서 다운로드하기" + }, + "onIos": { + "1": "iOS에서 다운로드하려면 다운로드 버튼을 클릭한 다음 새 페이지에서 를 클릭한 다음 파일로 저장 을 클릭합니다.", + "shortTitle": "다운로드 / iOS", + "title": "iOS에서 다운로드하기" + }, + "onPc": { + "1": "PC에서는 다운로드 버튼을 클릭한 다음 새 페이지에서 동영상을 마우스 오른쪽 버튼으로 클릭하고 다른 이름으로 동영상 저장을 선택합니다", + "shortTitle": "다운로드 / PC", + "title": "PC에서 다운로드하기" + }, + "title": "다운로드" + }, + "episodes": { + "button": "회차", + "emptyState": "이번 시즌에는 에피소드가 없으니 나중에 다시 확인하세요!", + "loadingError": "시즌 로딩중 에러 발생", + "loadingList": "로딩...", + "loadingTitle": "로딩..." + }, + "playback": { + "speedLabel": "재생 속도", + "title": "재생 설정" + }, + "quality": { + "automaticLabel": "자동 품질 제어", + "hint": "<0>소스 전환을 사용하여 다양한 화질 옵션을 확인할 수 있습니다.", + "iosNoQuality": "Apple에서 정의한 제한 사항으로 인해 이 소스에 대해 iOS에서 품질 선택을 사용할 수 없습니다. <0>다른 소스로 전환하여 다른 품질 옵션을 사용해 볼 수 있습니다.", + "title": "화질" + }, + "settings": { + "downloadItem": "다운로드", + "enableSubtitles": "자막 활성화", + "experienceSection": "시청 경험", + "playbackItem": "재생 설정", + "qualityItem": "화질", + "sourceItem": "영상 소스", + "subtitleItem": "자막 설정", + "videoSection": "영상 설정" + }, + "sources": { + "failed": { + "text": "동영상을 찾는 동안 오류가 발생했습니다. 다른 소스를 사용해 보세요.", + "title": "스크래핑 실패" + }, + "noEmbeds": { + "text": "임베드를 찾을 수 없습니다. 다른 소스를 사용해 보세요.", + "title": "발견된 임베드없음" + }, + "noStream": { + "text": "이 소스에는 해당 영화 또는 프로그램에 대한 스트림이 없습니다.", + "title": "발견된 스트림 없음" + }, + "title": "소스", + "unknownOption": "알수없음" + }, + "subtitles": { + "customChoice": "파일에서 자막 선택하기", + "customizeLabel": "사용자 정의", + "offChoice": "끔", + "settings": { + "backlink": "자막 커스텀", + "delay": "자막 딜레이", + "fixCapitals": "대문자 표기 수정" + }, + "title": "자막", + "unknownLanguage": "알수없음" + } + }, + "metadata": { + "failed": { + "badge": "실패함", + "homeButton": "메인으로 돌아가기", + "text": "TMDB에서 미디어의 메타데이터를 로드할 수 없습니다. 인터넷 연결에서 TMDB가 다운되었거나 차단되었는지 확인하세요.", + "title": "메타데이터를 불러오지 못했습니다" + }, + "notFound": { + "badge": "찾을 수 없음", + "homeButton": "메인으로 돌아가기", + "text": "요청하신 미디어를 찾을 수 없습니다. 미디어가 삭제되었거나 사용자가 URL을 변조했습니다.", + "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": { + "remaining": "{{timeLeft}} 남음 • {{timeFinished, datetime}}에 종료" + } + }, + "screens": { + "dmca": { + "text": "sudo-flix의 DMCA 문의 페이지에 오신 것을 환영합니다! 당사는 지적 재산권을 존중하며 저작권 관련 문제를 신속하게 해결하고자 합니다. 귀하의 저작권이 있는 저작물이 당사 플랫폼에서 부적절하게 사용되었다고 생각되는 경우, 아래 이메일로 자세한 DMCA 통지를 보내주시기 바랍니다. 저작권이 있는 자료에 대한 설명, 연락처 정보, 선의의 신념을 담은 진술서를 포함하시기 바랍니다. 당사는 이러한 문제를 신속하게 해결하기 위해 최선을 다하고 있으며, sudo-flix을 창의성과 저작권을 존중하는 공간으로 유지하는 데 협조해 주셔서 감사합니다." + }, + "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": "프로필 색상 1", + "secondColor": "프로필 색상 2", + "title": "프로필 이미지 수정", + "userIcon": "유저 아이콘" + }, + "register": { + "cta": "시작하기", + "text": "기기 간에 시계 진행 상황을 공유하고 동기화 상태를 유지하세요.", + "title": "클라우드에 동기화" + }, + "title": "계정" + }, + "appearance": { + "activeTheme": "활성화", + "themes": { + "blue": "블루", + "default": "기본", + "gray": "그레", + "red": "레드", + "teal": "청록" + }, + "title": "외관" + }, + "connections": { + "server": { + "description": "사용자 지정 백엔드에 연결하여 데이터를 저장하려면 이 기능을 활성화하고 URL을 입력합니다.", + "label": "사용자 지정 서버", + "urlLabel": "사용자 지정 서버 URL" + }, + "title": "연결", + "workers": { + "addButton": "새 워커 추가하기", + "description": "애플리케이션이 작동하도록 하기 위해 모든 트래픽은 프록시를 통해 라우팅됩니다. 직접 워커를 사용하려면 이 옵션을 사용 설정하세요.", + "emptyState": "아직 워커가 없는 경우 아래에서 워커를 추가하세요", + "label": "사용자 지정 프록시 워커 사용", + "urlLabel": "워커 URL" + } + }, + "reset": "초기화", + "save": "저장", + "sidebar": { + "info": { + "appVersion": "앱 버전", + "backendUrl": "백엔드 URL", + "backendVersion": "백엔드 버전", + "hostname": "호스트명", + "insecure": "안전하지 않음", + "notLoggedIn": "로그인하지 않으셨습니다", + "secure": "안전함", + "title": "앱 정보", + "unknownVersion": "알 수 없음", + "userId": "유저 ID" + } + }, + "subtitles": { + "backgroundLabel": "배경 투명도", + "colorLabel": "색상", + "previewQuote": "두려워해서는 안 됩니다. 두려움은 마음을 죽이는 존재입니다.", + "textSizeLabel": "글자 크기", + "title": "자막" + }, + "unsaved": "저장하지 않은 변경 사항이 있습니다" + } +} diff --git a/src/assets/locales/lv.json b/src/assets/locales/lv.json new file mode 100644 index 00000000..7dfacbe0 --- /dev/null +++ b/src/assets/locales/lv.json @@ -0,0 +1,389 @@ +{ + "about": { + "description": "sudo-flix ir tīmekļa lietojumprogramma, kas internetā meklē straumes. Komandas mērķis ir galvenokārt minimālistiska pieeja satura patērēšanai.", + "faqTitle": "Bieži jautājumi", + "q1": { + "body": "sudo-flix neveic nekādu saturu. Noklikšķinot uz kāda skatāma satura, internetā tiek meklēts atlasītais multivides saturs (ielādes ekrānā un cilnē “video avoti” varat redzēt, kuru avotu izmantojat). Multivide nekad netiek augšupielādēta, izmantojot filmu tīmekli, viss notiek caur šo meklēšanas mehānismu.", + "title": "No kurienes nāk saturs?" + }, + "q2": { + "body": "Nav iespējams pieprasīt pārraidi vai filmu, filmu tīmeklis nepārvalda saturu. Viss saturs tiek skatīts, izmantojot avotus internetā.", + "title": "Kur es varu pieprasīt filmu vai seriālu?" + }, + "q3": { + "body": "Mūsu meklēšanas rezultātus nodrošina filmu datu bāze (TMDB), un tie tiek rādīti neatkarīgi no tā, vai mūsu avotos patiešām ir saturs.", + "title": "Meklēšanas rezultātos tiek parādīta seriāls vai filma. Kāpēc es nevaru to atskaņot?" + }, + "title": "Par sudo-flix" + }, + "actions": { + "copied": "Nokopēts", + "copy": "Kopēt" + }, + "auth": { + "createAccount": "Nav vēl konts? <0>Taisīt kontu.", + "deviceNameLabel": "Ierīces nosaukums", + "deviceNamePlaceholder": "Personiskais telefons", + "generate": { + "description": "Tava paroles frāze ir kā vārds un parole. Esi drošs ka turi to drošibā jo tev vajadzēs to izmantot lai ieietu kontā", + "next": "Esmu saglabājis paroles frāzi", + "title": "Tava paroles frāze" + }, + "hasAccount": "Tev jau ir konts> <0>Ienāc šeit.", + "login": { + "description": "Lūdzu ievadi paroles frāzi lai ieietu kontā", + "deviceLengthError": "Lūdzu ievadi ierīces nosaukumu", + "passphraseLabel": "12-Vārdu paroles frāze", + "passphrasePlaceholder": "Paroles frāze", + "submit": "Ieiet", + "title": "Reģistrējies savā kontā", + "validationError": "Nepareizs vai nepabeigts paroles frāze" + }, + "register": { + "information": { + "color1": "Profila krāsa viens", + "color2": "Profila krāsa divi", + "header": "Ievadi ierīces vārdu un izvēlies krāsu un profila ikonu kādu gribi", + "icon": "Profila ikona", + "next": "Nakamais", + "title": "Konta informācija" + } + }, + "trust": { + "failed": { + "text": "Vai tu konfigurēji to pareizi?", + "title": "Neizdevās savienot serveri" + }, + "host": "Tu piesaisties <0>{{hostname}} - apstiprini ka uzticies pirms taisi kontu", + "no": "Atpakaļ", + "title": "Vai tu uzticies šim serverim?", + "yes": "Es uzticos šim serverim" + }, + "verify": { + "description": "Lūdzu ievadi paroles frāzi ko ieguvi iepriekš lai apstiprinātu ka saglabāji un uztaisītu kontu", + "invalidData": "Dati nav pieejami", + "noMatch": "paroles frāze nesakrīt", + "passphraseLabel": "Tava 12-vārdu paroles frāze", + "recaptchaFailed": "ReCaptcha apstiprināšana neizdevās", + "register": "Izveidot kontu", + "title": "Apstiprini paroles frāzi" + } + }, + "errors": { + "badge": "Saplīsa", + "details": "Kļūdas deteļas", + "reloadPage": "Atjaunot lapu", + "showError": "Radīt kļūdas deteļas", + "title": "Mēs sastapāmies ar kļūdu!" + }, + "footer": { + "legal": { + "disclaimer": "Atruna", + "disclaimerText": "sudo-flix nemitina nekādus failus, tas tikai veido saites uz trešās puses pakalpojumiem. Juridiskie jautājumi ir jārisina ar failu resursdatoriem un nodrošinātājiem. sudo-flix nav atbildīgs par video pakalpojumu sniedzēju parādītajiem multivides failiem." + }, + "links": { + "discord": "Discord", + "dmca": "Autortiesību likums", + "github": "GitHub" + }, + "tagline": "Skatieties savas iecienītākās pārraides un filmas, izmantojot šo atvērtā koda straumēšanas lietotni." + }, + "global": { + "name": "Filmas-web", + "pages": { + "about": "Par", + "dmca": "Autortiesību likums", + "login": "Ieiet", + "pagetitle": "{{title}} - filmas-web", + "register": "Reģistrēties", + "settings": "Iestādijumi" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Grāmatzīmes" + }, + "continueWatching": { + "sectionTitle": "Turpini skatīties" + }, + "mediaList": { + "stopEditing": "Pārtraukt rediģēšanu" + }, + "search": { + "allResults": "Tass ir viss kas mums ir!", + "failed": "Neizdevās atrast multividi. Mēģiniet vēlreiz!", + "loading": "Lādejas...", + "noResults": "Mēs nevarējām neko atrast!", + "placeholder": "Ko tu gribi skatīties?", + "sectionTitle": "Meklējuma rezultāti" + }, + "titles": { + "day": { + "default": "Ko jūs vēlētos noskatīties šajā pēcpusdienā?" + }, + "morning": { + "default": "Ko tu gribētu šorīt noskatīties?", + "extra": ["Es dzirdu, ka Pirms saullēkta ir labs"] + }, + "night": { + "default": "Ko tu gribētu šovakar skatīties?", + "extra": ["Noguris? Es dzirdu, ka Exorcist ir labs."] + } + } + }, + "media": { + "episodeDisplay": "Sezona{{season}} Episode{{episode}}", + "types": { + "movie": "Filma", + "show": "seriāls" + } + }, + "navigation": { + "banner": { + "offline": "Pārbaudiet interneta savienojumu" + }, + "menu": { + "about": "Par mums", + "donation": "Ziedot", + "logout": "Iziet", + "register": "Sinhronizēt ar mākoni", + "settings": "Iestādijumi", + "support": "Atbalsts" + } + }, + "notFound": { + "badge": "Nav atrasts", + "goHome": "Atpakaļ uz majām", + "message": "Mēs apskatijāmies visur: zem miskastes, skapī, aiz proxija bet nevarejām atrast lapu ko tu meklēji.", + "title": "Nevarēja atrast lapu" + }, + "overlays": { + "close": "Aizvērt" + }, + "player": { + "back": { + "default": "Atpakaļ uz mājām", + "short": "Atpakaļ" + }, + "casting": { + "enabled": "Atskaņo uz ierīci..." + }, + "menus": { + "downloads": { + "disclaimer": "Lejupielādes tiek ņemtas tieši no pakalpojumu sniedzēja. sudo-flix nevar kontrolēt, kā tiek nodrošinātas lejupielādes.", + "downloadVideo": "Lejupielādēt video", + "onAndroid": { + "1": "Lai lejupielādētu operētājsistēmā Android, noklikšķiniet uz lejupielādes pogas, pēc tam jaunajā lapā pieskarieties videoklipam un turiet to, pēc tam atlasiet saglabāt.", + "shortTitle": "Lejupielādēt / Android", + "title": "Lejupielāde operētājsistēmā Android" + }, + "onIos": { + "1": "Lai lejupielādētu operētājsistēmā iOS, noklikšķiniet uz lejupielādes pogas, pēc tam jaunajā lapā noklikšķiniet uz un pēc tam uz Saglabāt failos .", + "shortTitle": "Lejupielādēt / iOS", + "title": "Lejupielāde operētājsistēmā iOS" + }, + "onPc": { + "1": "Datorā noklikšķiniet uz lejupielādes pogas, pēc tam jaunajā lapā ar peles labo pogu noklikšķiniet uz videoklipa un atlasiet Saglabāt video kā", + "shortTitle": "Lejupielādēt / datorā", + "title": "Lejupielāde datorā" + }, + "title": "Lejupladēt" + }, + "episodes": { + "button": "Episodes", + "emptyState": "Šajā sezonā nav nevienas sērijas. Pārbaudiet vēlāk!", + "episodeBadge": "E{{episode}}", + "loadingError": "Kļūda ladējot sezonu", + "loadingList": "Lādejas...", + "loadingTitle": "Lādejas..." + }, + "playback": { + "speedLabel": "Atskaņošana ātrums", + "title": "Atskaņošana iestādijumi" + }, + "quality": { + "automaticLabel": "Automātiskā kvalitāte", + "hint": "Varat mēģināt <0>pārslēgt avotu, lai iegūtu dažādas kvalitātes opcijas.", + "iosNoQuality": "Apple noteikto ierobežojumu dēļ šim avotam iOS nav pieejama kvalitātes izvēle. Varat mēģināt <0>pārslēgties uz citu avotu, lai iegūtu dažādas kvalitātes opcijas.", + "title": "Kvalitāte" + }, + "settings": { + "downloadItem": "Lejupladēt", + "experienceSection": "Skatīšanās pieredze", + "playbackItem": "Atskaņošana iestādijumi", + "qualityItem": "Kvalitāte", + "sourceItem": "Video avoti", + "videoSection": "Video iestādijumi" + }, + "sources": { + "failed": { + "text": "Mēģinot atrast videoklipus, radās kļūda. Lūdzu, mēģiniet izmantot citu avotu.", + "title": "Neizdevās nokasīt" + }, + "noEmbeds": { + "text": "Mēs nevarējām atrast nevienu iegulšanu. Lūdzu, mēģiniet izmantot citu avotu.", + "title": "Netika atrasta neviena iegulšana" + }, + "noStream": { + "text": "Šim avotam nav šīs filmas vai pārraides straumju.", + "title": "Nav streama" + }, + "title": "Avoti", + "unknownOption": "Nezināms" + } + }, + "metadata": { + "failed": { + "badge": "Neizdevās", + "homeButton": "iet uz majām", + "text": "Nevarēja ielādēt multivides metadatus no TMDB. Lūdzu, pārbaudiet, vai TMDB nedarbojas vai nav bloķēts jūsu interneta savienojumā.", + "title": "Neizdevās ielādēt metadatus" + }, + "notFound": { + "badge": "Nav atrasts", + "homeButton": "Atpakaļ uz mājām", + "text": "Mēs nevarējām atrast jūsu pieprasīto multividi. Vai nu tas ir noņemts, vai arī jūs esat mainījis URL.", + "title": "Nevarēja atrast mēdiju." + } + }, + "nextEpisode": { + "cancel": "Atcelt", + "next": "Nakamā epizode" + }, + "playbackError": { + "badge": "Atskaņošanas kļūda", + "errors": { + "errorAborted": "Multivides iegūšana tika pārtraukta pēc lietotāja pieprasījuma.", + "errorDecode": "Lai gan iepriekš tika noteikts, ka tas ir lietojams, mēģinot atšifrēt multivides resursu, radās kļūda, kā rezultātā radās kļūda.", + "errorGenericMedia": "Nezināma medijas kļūda paradijās.", + "errorNetwork": "Radās sava veida tīkla kļūda, kas neļāva veiksmīgi ielādēt multividi, lai gan tas iepriekš bija pieejams.", + "errorNotSupported": "Multivides vai multivides nodrošinātāja objekts netiek atbalstīts." + }, + "homeButton": "iet majās", + "text": "Notika kļūda kamēr meiģināja atskaņot video. Lūdzu meiģini atkal.", + "title": "Neizdevās palaist video!" + }, + "scraping": { + "items": { + "failure": "Parādijās kļūda", + "notFound": "Nav šī video", + "pending": "Pārbauda priekš video..." + }, + "notFound": { + "badge": "Nav atrasts", + "detailsButton": "seriāls deteļas", + "homeButton": "Iet majās", + "text": "Mēs meklejām cour mūsu piedavatājiem un nevarējām atrast ko tu meklēji! Mēs nehostojam mediju un mums nav kontroles kas ir pieejams. Lūdzu uzpied 'šova deteļas' apakšā priekš vairāk deteļām.", + "title": "Mēs nevarējām atrast to" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} beidza • pabeidza {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + } + }, + "screens": { + "dmca": { + "text": "Laipni lūdzam filmu-web Autortiesību kontaktu lapā! Mēs cienām intelektuālā īpašuma tiesības un vēlamies ātri atrisināt visas autortiesību problēmas. Ja uzskatāt, ka jūsu ar autortiesībām aizsargātais darbs ir nepareizi izmantots mūsu platformā, lūdzu, nosūtiet uz tālāk norādīto e-pasta ziņojumu detalizētu Digitālās tūkstošgades autortiesību likuma paziņojumu. Lūdzu, iekļaujiet ar autortiesībām aizsargātā materiāla aprakstu, savu kontaktinformāciju un labas ticības apliecinājumu. Mēs esam apņēmušies ātri atrisināt šīs problēmas un novērtējam jūsu sadarbību, lai saglabātu filmu tīmekli par vietu, kurā tiek ievērotas radošums un autortiesības.", + "title": "Autortiesību likums" + }, + "loadingApp": "Notiek aplikācijas ielāde", + "loadingUser": "Notiek jūsu profila ielāde", + "loadingUserError": { + "logout": "iziet", + "reset": "Atiestatīt pielāgoto serveri", + "text": "Neizdevās ieladēt jūsu profilu", + "textWithReset": "Neizdevās ielādēt profilu no pielāgotā servera. Vai vēlaties atiestatīt atpakaļ uz noklusējuma serveri?" + }, + "migration": { + "failed": "Neizdevās migrēt jūsu datus.", + "inProgress": "Lūdzu, uzgaidiet, mēs migrējam jūsu datus. Tam nevajadzētu ilgt ilgu laiku." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Ierīces vārds", + "deviceNamePlaceholder": "Personiskais telefons", + "editProfile": "Reģistrēt", + "logoutButton": "Iziet" + }, + "actions": { + "delete": { + "button": "Dzēst kontu", + "confirmButton": "Dzēst kontu", + "confirmDescription": "Vai tiešām vēlaties dzēst savu kontu? Visi jūsu dati tiks zaudēti!", + "confirmTitle": "Vai tu esi pārliecināts?", + "text": "Šī darbība ir neatgriezeniska. Visi dati tiks dzēsti, un neko nevarēs atgūt.", + "title": "Dzēst kontu" + }, + "title": "Darbības" + }, + "devices": { + "deviceNameLabel": "Ierīces vārds", + "failed": "Neizdevās ielādēt sesijas", + "removeDevice": "Nonēmt", + "title": "Ierīces" + }, + "profile": { + "finish": "Pabeidziet rediģēšanu", + "firstColor": "Profila krāsa viens", + "secondColor": "Profila krāsa divi", + "title": "Mainīt profila bildi", + "userIcon": "Konta ikona" + }, + "register": { + "cta": "Sākt", + "text": "Kopīgojiet pulksteņa progresu starp ierīcēm un sinhronizējiet tās.", + "title": "Sinhronizēt ar mākoni" + }, + "title": "Konts" + }, + "appearance": { + "activeTheme": "Aktīvs", + "themes": { + "blue": "Zils", + "default": "Parasts", + "gray": "Pelēks", + "red": "Sarkans", + "teal": "zilganzaļš" + }, + "title": "Izskats" + }, + "connections": { + "server": { + "description": "Ja vēlaties izveidot savienojumu ar pielāgotu aizmugursistēmu, lai saglabātu savus datus, iespējojiet to un norādiet URL.", + "label": "Pielāgots serveris", + "urlLabel": "Pielāgota servera URL" + }, + "title": "Savienojumi", + "workers": { + "addButton": "pievienot jaunu worker", + "description": "Lai lietojumprogramma darbotos, visa trafika tiek maršrutēta caur starpniekserveriem. Iespējojiet šo, ja vēlaties piesaistīt savus darbiniekus.", + "emptyState": "Vēl nav workers. Pievienojiet vienu zemāk", + "label": "Izmantojiet pielāgotus starpniekserverus", + "urlLabel": "Worker URLs", + "urlPlaceholder": "https://" + } + }, + "reset": "Restartēt", + "save": "Saglabāt", + "sidebar": { + "info": { + "appVersion": "Aplikācijas versija", + "backendUrl": "Aizmugursistēmas URL", + "backendVersion": "Aizmugurējā versija", + "hostname": "Saimniekdatora nosaukums", + "insecure": "Nedrošs", + "notLoggedIn": "Jūs neesat pievienojies", + "secure": "Drošs", + "title": "Aplikācijas informācija", + "unknownVersion": "Nezināms", + "userId": "Lietotāja ID" + } + }, + "unsaved": "Jums ir nesaglabātas izmaiņas" + } +} diff --git a/src/assets/locales/minion.json b/src/assets/locales/minion.json new file mode 100644 index 00000000..038fbb1a --- /dev/null +++ b/src/assets/locales/minion.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "Minion-flix 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": { + "body": "Minion-flix 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-flix, everything is banana this banana mechanism.", + "title": "Where does the banana come from?" + }, + "q2": { + "body": "It's not banana to banana a banana or banana, Minion-flix does not banana any banana. All banana is banana through bananas on the banana.", + "title": "Banana can I banana a banana or banana?" + }, + "q3": { + "body": "Our banana results are banana by The Banana Banana (TBMB) and banana regardless of whether our bananas actually have the banana.", + "title": "The banana results banana the banana or banana, banana can't I banana it?" + }, + "title": "About Minion-flix" + }, + "actions": { + "copied": "Banana", + "copy": "Banana" + }, + "auth": { + "createAccount": "Whaaaat? Don't have an account yet? <0>Create an account.", + "deviceNameLabel": "Device name", + "deviceNamePlaceholder": "Banana phone", + "generate": { + "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", + "next": "I have saved my banana passphrase", + "passphraseFrameLabel": "Bananaphrase", + "title": "Your banana passphrase" + }, + "hasAccount": "Bello! Already have an account? <0>Login here.", + "login": { + "description": "Please enter your secret banana language passphrase to login to your account", + "deviceLengthError": "Banana! Please enter a device name", + "passphraseLabel": "12-Banana passphrase", + "passphrasePlaceholder": "Banana Passphrase", + "submit": "Bello! Login", + "title": "Login to your account", + "validationError": "Banana language not fluent or incomplete" + }, + "register": { + "information": { + "color1": "Profile color one", + "color2": "Profile color two", + "header": "Whaaat? Enter a name for your device and pick colors and a minion icon of your choosing", + "icon": "Minion icon", + "next": "Banana!", + "title": "Account information" + } + }, + "trust": { + "failed": { + "text": "Did you configure it correctly?", + "title": "Failed to reach server" + }, + "host": "You are connecting to <0>{{hostname}} - please confirm you trust it before making a banana account", + "no": "Go back, banana", + "title": "Do you trust this server?", + "yes": "I trust this server, banana!" + }, + "verify": { + "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", + "passphraseLabel": "Your 12-banana passphrase", + "recaptchaFailed": "Banana! ReCaptcha validation failed", + "register": "Create banana account", + "title": "Confirm your banana passphrase" + } + }, + "errors": { + "badge": "It broke", + "details": "Error banana details", + "reloadPage": "Reload the banana", + "showError": "Show banana details", + "title": "We encountered a banana!" + }, + "footer": { + "legal": { + "disclaimer": "Banana", + "disclaimerText": "Minion-flix 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-flix is not banana for any banana bananas shown by the banana bananas." + }, + "links": { + "discord": "Banana", + "dmca": "Banana", + "github": "Banana" + }, + "tagline": "Banana your favourite bananas and bananas with this open source banana app." + }, + "global": { + "name": "banana-web", + "pages": { + "about": "About banana", + "dmca": "DMCA", + "login": "Banana Login", + "pagetitle": "{{title}} - banana-web", + "register": "Banana Register", + "settings": "Banana Settings" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Banana" + }, + "continueWatching": { + "sectionTitle": "Continue Banana" + }, + "mediaList": { + "stopEditing": "Stop banana" + }, + "search": { + "allResults": "Banana's all we banana!", + "failed": "Failed to banana banana, try again!", + "loading": "Loading...", + "noResults": "We couldn't banana anything!", + "placeholder": "Banana do you want to banana?", + "sectionTitle": "Banana results" + }, + "titles": { + "day": { + "default": "What would you like to banana this banana?", + "extra": [ + "Feeling banana? Jurassic banana banana banana banana perfect banana." + ] + }, + "morning": { + "default": "What would you like to banana this banana?", + "extra": ["Banana! I hear Banana Sunrise is banana"] + }, + "night": { + "default": "What would you like to banana banana?", + "extra": ["Banana? I hear The Banana is banana."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Banana Movie", + "show": "Banana Show" + } + }, + "navigation": { + "banner": { + "offline": "Check your banana connection" + }, + "menu": { + "about": "Banana us", + "donation": "Banana", + "logout": "Banana out", + "register": "Banana to banana", + "settings": "Banana", + "support": "Banana" + } + }, + "notFound": { + "badge": "Not banana", + "goHome": "Back to 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.", + "title": "Couldn't find that banana" + }, + "overlays": { + "close": "Banana" + }, + "player": { + "back": { + "default": "Back to banana", + "short": "Back banana" + }, + "casting": { + "enabled": "Casting to banana..." + }, + "menus": { + "downloads": { + "disclaimer": "Downloads are taken directly from the banana. banana-web does not have banana over how the banana are banana.", + "downloadPlaylist": "Download banana", + "downloadSubtitle": "Download current banana", + "downloadVideo": "Banana", + "hlsDisclaimer": "Downloads are taken directly from the banana. Banana-web does not have control over how the downloads are banana. please note that you are downloading Banana playlist, this is intended for minions familiar with advanced multimedia banana.", + "onAndroid": { + "1": "To banana on Banana, click the banana banana then, on the new banana, tap and hold on the banana, then select banana.", + "shortTitle": "Banana / Banana", + "title": "Banana" + }, + "onIos": { + "1": "To banana on Banana, click the banana banana then, on the new banana, click , then Banana to banana .", + "shortTitle": "Banana / Banana", + "title": "Banana" + }, + "onPc": { + "1": "On PC, click the banana banana then, on the new banana, right click the banana and select Banana", + "shortTitle": "Banana / PC", + "title": "Banana" + }, + "title": "Banana" + }, + "episodes": { + "button": "Banana", + "emptyState": "There are no banana in this banana, check back banana!", + "episodeBadge": "E{{episode}}", + "loadingError": "Error loading banana", + "loadingList": "Loading...", + "loadingTitle": "Loading...", + "unairedEpisodes": "One or more banana in this banana have been banana because they haven't been aired yet." + }, + "playback": { + "speedLabel": "Banana speed", + "title": "Banana settings" + }, + "quality": { + "automaticLabel": "Banana", + "hint": "You can banana <0>banana 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 to get different banana banana.", + "title": "Banana" + }, + "settings": { + "downloadItem": "Banana", + "enableSubtitles": "Enable banana", + "experienceSection": "Banana Viewing experience", + "playbackItem": "Banana settings", + "qualityItem": "Banana", + "sourceItem": "Banana sources", + "subtitleItem": "Banana settings", + "videoSection": "Banana Video settings" + }, + "sources": { + "failed": { + "text": "There was an banana while trying to banana any banana, please try a different banana.", + "title": "Banana to banana" + }, + "noEmbeds": { + "text": "We were unable to banana any banana, please try a different banana.", + "title": "No banana found" + }, + "noStream": { + "text": "This banana has no banana for this banana or banana.", + "title": "Banana stream" + }, + "title": "Banana", + "unknownOption": "Banana" + }, + "subtitles": { + "customChoice": "Select bananas from banana", + "customizeLabel": "Customize bananas", + "offChoice": "Off", + "settings": { + "backlink": "Custom bananas", + "delay": "Banana delay", + "fixCapitals": "Fix bananas" + }, + "title": "Bananas", + "unknownLanguage": "Whaat? Unknown banana!" + } + }, + "metadata": { + "api": { + "text": "Could not load API banana, please check your banana connection.", + "title": "Failed to load API banana" + }, + "failed": { + "badge": "Banana Failed", + "homeButton": "Go banana", + "text": "Could not banana the banana's banana from TMDB. Please banana whether TMDB is down or banana on your banana connection.", + "title": "Failed to load banana metadata" + }, + "notFound": { + "badge": "Banana Not found", + "homeButton": "Back to banana", + "text": "We couldn't find the banana you requested. Either it's been banana or you tampered with the banana.", + "title": "Couldn't find that banana." + } + }, + "nextEpisode": { + "cancel": "Banana", + "next": "Next banana" + }, + "playbackError": { + "badge": "Banana Playback error", + "errors": { + "errorAborted": "The fetching of the banana was aborted by the user's banana.", + "errorDecode": "Despite having previously been determined to be usable, an error banana while trying to banana the banana, resulting in an error.", + "errorGenericMedia": "Unknown banana error occurred.", + "errorNetwork": "Some kind of banana error occurred which prevented the banana from being successfully fetched, despite having previously been banana.", + "errorNotSupported": "The banana or banana provider object is not banana." + }, + "homeButton": "Go home", + "text": "There was an error trying to play the banana. Please try again.", + "title": "Failed to play banana video!" + }, + "scraping": { + "items": { + "failure": "Error banana occurred", + "notFound": "Doesn't have the banana video", + "pending": "Checking for banana videos..." + }, + "notFound": { + "badge": "Not found", + "detailsButton": "Show details", + "homeButton": "Go home", + "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.", + "title": "We couldn't find that banana" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} left • Finish at {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Please verify that you are banana by completing the banana on the right. This is to keep banana-web banana!", + "error": "Failed to verify your banananess. Please try banana.", + "title": "We banana to verify that you're banana", + "verifyingHumanity": "Verifying your banana..." + } + }, + "screens": { + "dmca": { + "text": "Very long and boring banana.", + "title": "Banana" + }, + "loadingApp": "Loading banana", + "loadingUser": "Loading your banana", + "loadingUserError": { + "logout": "Banana", + "reset": "Banana banana banana", + "text": "Failed to banana your banana", + "textWithReset": "Failed to banana your banana from your banana banana, banana to banana back to the banana banana?" + }, + "migration": { + "failed": "Banana to banana your banana.", + "inProgress": "Please banana, we are banana your banana. This shouldn't banana long." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Banana name", + "deviceNamePlaceholder": "Banana phone", + "editProfile": "Banana", + "logoutButton": "Banana out" + }, + "actions": { + "delete": { + "button": "Banana", + "confirmButton": "Banana", + "confirmDescription": "Banana you banana to banana your banana? All your bananas will be banana!", + "confirmTitle": "Banana you banana?", + "text": "Whaaat? This banana is irreversible. All bananas will be banana and nothing can be banana.", + "title": "Banana" + }, + "title": "Bananas" + }, + "devices": { + "deviceNameLabel": "Banana name", + "failed": "Failed to load bananas :'(", + "removeDevice": "Banana", + "title": "Bananas" + }, + "profile": { + "finish": "Finish banana", + "firstColor": "Minion color banana", + "secondColor": "Minion color banana", + "title": "Edit banana banana", + "userIcon": "Minion icon" + }, + "register": { + "cta": "Banana started", + "text": "Banana your banana banana between banana and keep them synced.", + "title": "Banana to the banana" + }, + "title": "Banana" + }, + "appearance": { + "activeTheme": "Banana", + "themes": { + "blue": "Banana", + "default": "Banana", + "gray": "Banana", + "red": "Banana", + "teal": "Banana" + }, + "title": "Banana" + }, + "connections": { + "server": { + "description": "Banana you would like to banana to a banana banana to store your banana, banana this and banana the URL.", + "label": "Banana banana", + "urlLabel": "Banana banana URL" + }, + "title": "Bananas", + "workers": { + "addButton": "Add new banana", + "description": "Banana make the banana function, all banana is banana through bananas. Banana this if you banana to banana your own bananas.", + "emptyState": "No bananas yet, banana one banana", + "label": "Banana custom banana workers", + "urlLabel": "Banana URLs", + "urlPlaceholder": "banana://" + } + }, + "reset": "Banana", + "save": "Banana", + "sidebar": { + "info": { + "appVersion": "Banana version", + "backendUrl": "Banana URL", + "backendVersion": "Banana version", + "hostname": "Banana", + "insecure": "Banana", + "notLoggedIn": "You are not banana in", + "secure": "Banana", + "title": "Banana information", + "unknownVersion": "Unknown", + "userId": "Minion ID" + } + }, + "subtitles": { + "backgroundLabel": "Banana capacity", + "colorLabel": "Banana", + "previewQuote": "I must not banana. Banana is the minion-killer.", + "textSizeLabel": "Banana size", + "title": "Bananas" + }, + "unsaved": "Whaaat? You have unsaved bananas" + } +} diff --git a/src/assets/locales/ne.json b/src/assets/locales/ne.json new file mode 100644 index 00000000..9701bef4 --- /dev/null +++ b/src/assets/locales/ne.json @@ -0,0 +1,533 @@ +{ + "about": { + "description": "sudo-flix एउटा वेब एप हो जसले स्ट्रिमहरूको लागि इन्टरनेटमा खोज्छ। हाम्रा टोलीले सामग्री उपभोग गर्नको लागि प्रायः न्यूनतम दृष्टिकोणको लागि लक्ष्य राख्छ।", + "faqTitle": "सामान्य प्रश्नहरू", + "q1": { + "body": "sudo-flix ले कुनै पनि सामग्री होस्ट गर्दैन। जब तपाइँ हेर्नको लागि केहि क्लिक गर्नुहुन्छ, इन्टरनेटमा चयन गरिएको मिडियाको लागि खोजी गरिन्छ (लोडिङ स्क्रिनमा र 'भिडियो स्रोत' ट्याबमा तपाइँ कुन स्रोत प्रयोग गरिरहनु भएको छ भनेर देख्न सक्नुहुन्छ)। मिडिया कहिले पनि चलचित्र-वेब द्वारा अपलोड हुँदैन, सबै कुरा यो खोजी संयन्त्र मार्फत हुन्छ।", + "title": "सामग्री कहाँबाट आउँछ?" + }, + "q2": { + "body": "कार्यक्रम वा चलचित्र अनुरोध गर्न सम्भव छैन, sudo-flixले कुनै पनि सामग्री व्यवस्थापन गर्दैन। सबै सामग्री इन्टरनेटमा स्रोतहरू मार्फत हेरिन्छ।", + "title": "म कहाँ कार्यक्रम वा चलचित्र अनुरोध गर्न सक्छु?" + }, + "q3": { + "body": "हाम्रा खोज परिणामहरू चलचित्र डाटाबेस (TMDB) द्वारा संचालित हुन्छन् र हाम्रा स्रोतहरूमा साँच्चै सामग्री छ कि छैन भनी प्रदर्शन गरिन्छ।", + "title": "खोज परिणामहरूले कार्यक्रम वा चलचित्र प्रदर्शन गर्दछ, म यसलाई किन प्ले गर्न सक्दिन?" + }, + "title": "sudo-flix बारेमा" + }, + "actions": { + "copied": "कपी भयो", + "copy": "कपी" + }, + "auth": { + "createAccount": "अझै खाता छैन?<0>खाता खोल्नुहोस्|.", + "deviceNameLabel": "उपकरणको नाम", + "deviceNamePlaceholder": "निजी फोन", + "generate": { + "description": "तपाईंको पासफ्रेजले तपाईंको प्रयोगकर्ता नाम र पासवर्डको रूपमा कार्य गर्दछ। यसलाई सुरक्षित राख्नुहोस् किनकि तपाईंले आफ्नो खातामा लगइन गर्न आवश्यक हुनेछ", + "next": "मैले मेरो पासफ्रेज सुरक्षित गरेको छु", + "passphraseFrameLabel": "पासफ्रेज", + "title": "तपाईको पासफ्रेज" + }, + "hasAccount": "पहिले नै खाता छ? <0>यहाँ लग-इन गर्नुहोस्|", + "login": { + "description": "कृपया आफ्नो खातामा लगइन गर्नको लागि आफ्नो पासफ्रेज हाल्नुहोस", + "deviceLengthError": "कृपया फोनको नाम हाल्नुहोस", + "passphraseLabel": "१२-शब्द पासफ्रेज", + "passphrasePlaceholder": "पासफ्रेज", + "submit": "लगइन", + "title": "आफ्नो खातामा लगइन गर्नुहोस्", + "validationError": "गलत वा अपूर्ण पासफ्रेज" + }, + "register": { + "information": { + "color1": "प्रोफाइल रङ एक", + "color2": "प्रोफाइल रङ दुई", + "header": "आफ्नो फोनको लागि नाम लेख्नुहोस र रङ र चित्र छनौट गर्नुहोस", + "icon": "प्रयोगकर्ता चित्र", + "next": "अर्को", + "title": "खाता जानकारी" + } + }, + "trust": { + "failed": { + "text": "के तपाईंले यसलाई सही रूपमा कन्फिगर गर्नुभयो?", + "title": "सर्भरमा पुग्न असफल भयो" + }, + "host": "तपाइँ <0>{{hostname}} मा कनेक्ट हुनुहुन्छ - कृपया खाता बनाउनु अघि तपाइँ यसलाई विश्वास गर्नुहुन्छ भनेर पुष्टि गर्नुहोस्", + "no": "पछाडी जाउ", + "title": "के तपाइँ यो सर्भरमा भरोसा गर्नुहुन्छ?", + "yes": "म यो सर्भरलाई भरोसा गर्छु" + }, + "verify": { + "description": "तपाईंले यसलाई सुरक्षित गर्नुभएको छ भनी पुष्टि गर्न र आफ्नो खाता सिर्जना गर्नको लागि कृपया आफ्नो पासफ्रेज हालनुहोस्", + "invalidData": "डाटा मान्य छैन", + "noMatch": "पासफ्रेज मेल खाँदैन", + "passphraseLabel": "तपाईंको १२-शब्द पासफ्रेज", + "recaptchaFailed": "ReCaptcha प्रमाणीकरण असफल भयो", + "register": "खाता बनाउनुहोस्", + "title": "आफ्नो पासफ्रेज पुष्टि गर्नुहोस्" + } + }, + "errors": { + "badge": "यो बिग्रियो", + "details": "त्रुटि विवरण", + "reloadPage": "पेज फेरी लोड गर्नुहोस्", + "showError": "त्रुटि विवरण देखाउनुहोस्", + "title": "हामीले एउटा त्रुटिको सामना गर्यौं!" + }, + "footer": { + "legal": { + "disclaimer": "Disclaimer", + "disclaimerText": "sudo-flixले कुनै पनि फाइलहरू होस्ट गर्दैन, यसले केवल तेस्रो पक्ष सेवाहरूमा लिङ्क गर्दछ। कानुनी मुद्दाहरू फाइल होस्ट र प्रदायकहरूसँग लिनु पर्छ। चलचित्र-वेब भिडियो प्रदायकहरू द्वारा देखाइएका कुनै पनि मिडिया फाइलहरूको लागि जिम्मेवार छैन।" + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "यो खुला स्रोत स्ट्रिमिङ एपको साथ आफ्नो मनपर्ने शो र चलचित्रहरू हेर्नुहोस्।" + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "जानकारी", + "dmca": "DMCA", + "login": "लग - इन", + "onboarding": "सेटअप", + "pagetitle": "{{title}} - sudo-flix", + "register": "दर्ता", + "settings": "सेटिङ्स्" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "बुकमार्कहरू" + }, + "continueWatching": { + "sectionTitle": "हेर्न जारी राख्नुहोस्" + }, + "mediaList": { + "stopEditing": "सम्पादन रोक्नुहोस्" + }, + "search": { + "allResults": "हामीसँग यति मात्र छ!", + "failed": "मिडिया फेला पार्न असफल भयो, फेरि प्रयास गर्नुहोस्!", + "loading": "लोड गर्दै...", + "noResults": "हामीले केहि फेला पार्न सकेनौं!", + "placeholder": "तपाईं के हेर्न चाहनुहुन्छ?", + "sectionTitle": "खोज परिणामहरू" + }, + "titles": { + "day": { + "default": "तपाईं आज दिउँसो के हेर्न चाहनुहुन्छ?", + "extra": [ + "साहसिक महसुस गर्दै हुनुहुन्छ? जुरासिक पार्क उत्तम विकल्प हुन सक्छ।" + ] + }, + "morning": { + "default": "तपाई आज बिहान के हेर्न चाहनुहुन्छ?", + "extra": ["Before Sunrise राम्रो छ भन्ने सुन्छु"] + }, + "night": { + "default": "तपाईं आज राती के हेर्न चाहनुहुन्छ?", + "extra": ["थकित? मैले सुनेको छु The Exorcist राम्रो छ।"] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "चलचित्र", + "show": "कार्यक्रम" + } + }, + "navigation": { + "banner": { + "offline": "आफ्नो इन्टरनेट जाँच गर्नुहोस्" + }, + "menu": { + "about": "हाम्रो बारे जानकारी", + "donation": "दान गर्नुहोस्", + "logout": "बाहिर निस्कनु", + "register": "क्लाउडमा सिंक गर्नुहोस्", + "settings": "सेटिङ", + "support": "समर्थन गर्नुहोस्" + } + }, + "notFound": { + "badge": "फेला परेन", + "goHome": "होम् फिर्ता जानुहोस्", + "message": "हामीले जताततै हेर्यौं: डिब्बा मुनि, कोठरीमा, प्रोक्सी पछाडि तर अन्ततः तपाईंले खोजिरहनु भएको पेज फेला पार्न सकेनौं।", + "title": "त्यो पेज फेला पार्न सकेन" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "रद्द गर्नुहोस्", + "confirm": "डिफल्ट सेटअप प्रयोग गर्नुहोस्", + "description": "पूर्वनिर्धारित सेटअपमा उत्कृष्ट स्ट्रिमहरू छैनन् र असहनीय रूपमा ढिलो हुन सक्छ।", + "title": "के तपाईँ निश्चित हुनुहुन्छ?" + }, + "extension": { + "back": "पछाडी जाउ", + "explainer": "ब्राउजर एक्सटेन्सन प्रयोग गरेर, तपाईंले हामीले प्रस्ताव गर्नु पर्ने उत्तम स्ट्रिमहरू प्राप्त गर्न सक्नुहुन्छ। केवल एक साधारण स्थापना संग।", + "explainerIos": "दुर्भाग्यवश, ब्राउजर एक्सटेन्सन IOS मा समर्थित छैन, अर्को विकल्प रोज्न Go back थिच्नुहोस्।", + "extensionHelp": "यदि तपाईंले एक्स्टेन्सन स्थापना गर्नुभएको छ तर यो पत्ता लागेको छैन। तपाईंको ब्राउजर विस्तार मेनु मार्फत विस्तार खोल्नुहोस् र स्क्रिनमा चरणहरू पालना गर्नुहोस्।", + "linkChrome": "क्रोम एक्सटेन्सन स्थापना गर्नुहोस्", + "linkFirefox": "फायरफक्स एक्सटेन्सन स्थापना गर्नुहोस्", + "notDetecting": "chrome मा स्थापित तर देखिदैन? पृष्ठ पुन: लोड गर्ने प्रयास गर्नुहोस्!", + "notDetectingAction": "पृष्ठ पुन: लोड गर्नुहोस्", + "status": { + "disallowed": "यो पेजको लागि एक्सटेन्सन सक्षम गरिएको छैन", + "disallowedAction": "एक्स्टेन्सन सक्षम गर्नुहोस्", + "failed": "स्थिति अनुरोध गर्न असफल", + "loading": "तपाइँ एक्सटेन्सन स्थापना गर्न को लागी प्रतिक्षा गर्दै", + "outdated": "एक्स्टेन्सन धेरै पुरानो छ", + "success": "एक्सटेन्सनले अपेक्षा गरे अनुसार काम गरिरहेको छ!" + }, + "submit": "जारी राख्नुहोस्", + "title": "एक्सटेन्सन संग सुरु गरौं" + }, + "proxy": { + "back": "पछाडी जाउ", + "explainer": "प्रोक्सी विधिको साथ, तपाईं स्वयं-सेवा प्रोक्सी बनाएर उत्कृष्ट गुणस्तर स्ट्रिमहरू प्राप्त गर्न सक्नुहुन्छ।", + "input": { + "errorConnection": "प्रोक्सीमा कनेक्ट हुन सकेन", + "errorInvalidUrl": "मान्य URL होइन", + "errorNotProxy": "प्रोक्सीको अपेक्षा गरे तर वेबसाइट पायो", + "label": "प्रोक्सी URL", + "placeholder": "https://" + }, + "link": "प्रोक्सी कसरी बनाउने सिक्नुहोस्", + "submit": "प्रोक्सी पेश गर्नुहोस्", + "title": "एउटा नयाँ प्रोक्सी बनाऔं" + }, + "start": { + "explainer": "सम्भावित उत्तम स्ट्रिमहरू प्राप्त गर्न,। तपाईंले कुन स्ट्रिमिङ विधि प्रयोग गर्न चाहनुहुन्छ भनेर छनौट गर्न आवश्यक हुनेछ।", + "options": { + "default": { + "text": "मलाई राम्रो गुणस्तरका स्ट्रिमहरू चाहिँदैन,<0 /> <1>पूर्वनिर्धारित सेटअप प्रयोग गर्नुहोस्" + }, + "extension": { + "action": "एक्सटेन्सन हाल्नुहोस", + "description": "ब्राउजर एक्सटेन्सन हाल्नुहोस र उत्तम स्रोतहरूमा पहुँच प्राप्त गर्नुहोस्।", + "quality": "उत्कृस्ट गुणस्तर", + "title": "ब्राउजर एक्सटेन्सन" + }, + "proxy": { + "action": "सेटअप प्रोक्सी", + "description": "केवल 5 मिनेटमा प्रोक्सी सेटअप गर्नुहोस् र उत्कृष्ट स्रोतहरूमा पहुँच प्राप्त गर्नुहोस्।", + "quality": "राम्रो क्वालिटी", + "title": "आफ्नै प्रोक्सी" + } + }, + "title": "तपाईँलाई sudo-flix सँग सेटअप गरौं" + } + }, + "overlays": { + "close": "बन्द गर्नुहोस्" + }, + "player": { + "back": { + "default": "घर फिर्ता", + "short": "फिर्ता" + }, + "casting": { + "enabled": "उपकरणमा कास्ट गर्दै..." + }, + "menus": { + "downloads": { + "disclaimer": "डाउनलोडहरू सीधा प्रदायकबाट लिइन्छ। sudo-flix ले डाउनलोडहरू कसरी प्रदान गरिन्छ भन्नेमा नियन्त्रण गर्दैन।", + "downloadPlaylist": "डाउनलोड प्लेलिस्ट", + "downloadSubtitle": "हालको उपशीर्षक डाउनलोड गर्नुहोस्", + "downloadVideo": "डाउनलोड भिडियो", + "hlsDisclaimer": "डाउनलोडहरू सीधा प्रदायकबाट लिइन्छ। चलचित्र-वेबको डाउनलोडहरू कसरी प्रदान गरिन्छ भन्नेमा नियन्त्रण छैन।

कृपया ध्यान दिनुहोस् कि तपाइँ HLS प्लेलिस्ट डाउनलोड गर्दै हुनुहुन्छ, यदि तपाइँ उन्नत स्ट्रिमिङ ढाँचाहरूसँग परिचित हुनुहुन्न भने यसलाई डाउनलोड गर्न सिफारिस गरिँदैन। । विभिन्न ढाँचाहरूको लागि विभिन्न स्रोतहरू प्रयास गर्नुहोस्।", + "onAndroid": { + "1": "एन्ड्रोइड मा, डाउनलोड बटन क्लिक गर्नुहोस् त्यसपछि, नयाँ पृष्ठमा, भिडियोमा ट्याप गर्नुहोस् र होल्ड गर्नुहोस्, त्यसपछि बचत चयन गर्नुहोस्।", + "shortTitle": "डाउनलोड / एन्ड्रोइड", + "title": "एन्ड्रोइडमा डाउनलोड हुदैछ" + }, + "onIos": { + "1": "iOS मा डाउनलोड गर्न, डाउनलोड बटन क्लिक गर्नुहोस् त्यसपछि, नयाँ पृष्ठमा, क्लिक गर्नुहोस्, त्यसपछि Save to Files ।", + "shortTitle": "डाउनलोड / iOS", + "title": "iOS मा डाउनलोड हुदैछ" + }, + "onPc": { + "1": "PC मा, डाउनलोड बटन क्लिक गर्नुहोस् त्यसपछि, नयाँ पृष्ठमा, भिडियोमा दायाँ क्लिक गर्नुहोस् र भिडियोलाई यस रूपमा सेव गर्नुहोस् चयन गर्नुहोस्", + "shortTitle": "डाउनलोड / कम्प्युटर", + "title": "कम्प्युटरमा डाउनलोड हुदैछ" + }, + "title": "डाउनलोड" + }, + "episodes": { + "button": "एपिसोडहरू", + "emptyState": "यस सिजनमा कुनै एपिसोडहरू छैनन्, पछि फेरि जाँच गर्नुहोस्!", + "episodeBadge": "E{{episode}}", + "loadingError": "सिजन लोड गर्दा त्रुटि भयो", + "loadingList": "लोड गर्दै...", + "loadingTitle": "लोड गर्दै...", + "unairedEpisodes": "यस सिजनका एक वा बढी एपिसोडहरू अझै प्रसारण नभएकाले असक्षम पारिएका छन्।" + }, + "playback": { + "speedLabel": "प्लेब्याकको गति", + "title": "प्लेब्याक सेटिङ" + }, + "quality": { + "automaticLabel": "स्वचालित क्वालिटी", + "hint": "तपाईं विभिन्न क्वालिटी प्राप्त गर्न <0>स्रोत स्विच प्रयास गर्न सक्नुहुन्छ।", + "iosNoQuality": "Apple-परिभाषित सीमितताहरूका कारण, गुणस्तर चयन यो स्रोतको लागि iOS मा उपलब्ध छैन। तपाईं विभिन्न गुणस्तर विकल्पहरू प्राप्त गर्न <0>अर्को स्रोतमा स्विच गर्न प्रयास गर्न सक्नुहुन्छ।", + "title": "क्वालिटी" + }, + "settings": { + "downloadItem": "डाउनलोड", + "enableSubtitles": "उपशीर्षकहरू सक्षम गर्नुहोस्", + "experienceSection": "हेर्ने अनुभव", + "playbackItem": "प्लेब्याक सेटिङ", + "qualityItem": "क्वालिटी", + "sourceItem": "भिडियो स्रोतहरू", + "subtitleItem": "उपशीर्षक सेटिङ", + "videoSection": "भिडियो सेत्तिन्ग्स" + }, + "sources": { + "failed": { + "text": "कुनै पनि भिडियोहरू फेला पार्न प्रयास गर्दा त्रुटि भयो, कृपया फरक स्रोत प्रयास गर्नुहोस्।", + "title": "फेला पार्न असफल भयो" + }, + "noEmbeds": { + "text": "हामीले कुनै पनि इम्बेडहरू फेला पार्न सकेनौं, कृपया अर्कै स्रोत प्रयास गर्नुहोस्।", + "title": "कुनै इम्बेडहरू फेला परेनन्" + }, + "noStream": { + "text": "यो स्रोतमा यो चलचित्र वा कार्यक्रमको लागि कुनै स्ट्रिमहरू छैनन्।", + "title": "कुनै स्ट्रिम छैन" + }, + "title": "स्रोतहरू", + "unknownOption": "अज्ञात" + }, + "subtitles": { + "customChoice": "फाइलबाट उपशीर्षक चयन गर्नुहोस्", + "customizeLabel": "अनुकूलन गर्नुहोस्", + "offChoice": "बन्द", + "settings": { + "backlink": "अनुकूलन उपशीर्षकहरू", + "delay": "उपशीर्षकको ढिलाइ", + "fixCapitals": "पूंजीकरण ठीक गर्नुहोस्" + }, + "title": "उपशीर्षक", + "unknownLanguage": "अज्ञात" + } + }, + "metadata": { + "api": { + "text": "API मेटाडेटा लोड गर्न सकिएन, कृपया आफ्नो इन्टरनेट जडान जाँच गर्नुहोस्।", + "title": "API मेटाडेटा लोड गर्न असफल भयो" + }, + "dmca": { + "badge": "हटाइयो", + "text": "यो मिडिया हटाउने सूचना वा प्रतिलिपि अधिकार दावीको कारणले अब उपलब्ध छैन।", + "title": "मिडिया हटाइएको छ" + }, + "extensionPermission": { + "badge": "अनुमति छैन", + "button": "एक्सटेन्सन प्रयोग गर्नुहोस्", + "text": "तपाईंसँग ब्राउजर एक्स्टेन्सन छ, तर हामीलाई एक्स्टेन्सन प्रयोग गर्न सुरु गर्न तपाईंको अनुमति चाहिन्छ।", + "title": "एक्सटेन्सन कन्फिगर गर्नुहोस्" + }, + "failed": { + "badge": "असफल", + "homeButton": "होम् जाउँ", + "text": "TMDB बाट मिडियाको मेटाडेटा लोड गर्न सकिएन। कृपया जाँच गर्नुहोस् कि TMDB डाउन वा तपाईंको इन्टरनेटमा प्रतिबन्धित छ।", + "title": "मेटाडेटा लोड गर्न असफल भयो" + }, + "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}}" + }, + "turnstile": { + "description": "कृपया दायाँपट्टि Captch पूरा गरेर तपाईं मानव हुनुहुन्छ भनी प्रमाणित गर्नुहोस्। यो sudo-flix लाई सुरक्षित राख्न को लागी हो!", + "error": "तपाईंको मानवता प्रमाणित गर्न असफल भयौ। फेरि प्रयास गर्नुहोस।", + "title": "हामीले तपाईं मानव हुनुहुन्छ भनेर प्रमाणित गर्न आवश्यक छ।", + "verifyingHumanity": "तपाईंको मानवता प्रमाणित हुदैछ..." + } + }, + "screens": { + "dmca": { + "text": "sudo-flix को DMCA सम्पर्क पृष्ठमा स्वागत छ! हामी बौद्धिक सम्पत्ति अधिकारको सम्मान गर्छौं र कुनै पनि प्रतिलिपि अधिकार सरोकारलाई तुरुन्तै सम्बोधन गर्न चाहन्छौं। यदि तपाइँ तपाइँको प्रतिलिपि अधिकार कार्य हाम्रो प्लेटफर्ममा अनुचित रूपमा प्रयोग भएको विश्वास गर्नुहुन्छ भने, कृपया तलको इमेलमा विस्तृत DMCA सूचना पठाउनुहोस्। कृपया प्रतिलिपि अधिकार सामग्रीको विवरण, तपाईंको सम्पर्क विवरणहरू, र राम्रो विश्वासको कथन समावेश गर्नुहोस्। हामी यी मामिलाहरू तुरुन्तै समाधान गर्न प्रतिबद्ध छौं र चलचित्र-वेबलाई रचनात्मकता र प्रतिलिपि अधिकारको सम्मान गर्ने ठाउँ राख्नमा तपाईंको सहयोगको कदर गर्छौं।", + "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": "उपकरणहरू बीच आफ्नो cप्रगति साझेदारी गर्नुहोस् र तिनीहरूलाई सिंक राख्नुहोस्।", + "title": "क्लाउडमा सिंक गर्नुहोस्" + }, + "title": "खाता" + }, + "appearance": { + "activeTheme": "सक्रिय", + "themes": { + "blue": "निलो", + "default": "साधारण", + "gray": "खैरो", + "red": "रातो", + "teal": "हरियो-नीलो" + }, + "title": "रूप-रंग" + }, + "connections": { + "server": { + "description": "यदि तपाईं आफ्नो डेटा भण्डारण गर्न अनुकूलन ब्याकइन्डमा जडान गर्न चाहनुहुन्छ भने, यसलाई सक्षम गर्नुहोस् र URL प्रदान गर्नुहोस्। <0>निर्देशनहरू।", + "label": "अनुकूलन सर्भर", + "urlLabel": "अनुकूलन सर्भर URL" + }, + "setup": { + "doSetup": "सेटअप गर्नुहोस्", + "errorStatus": { + "description": "यो सेटअपमा एक वा बढी वस्तुहरू तपाईंको ध्यान आवश्यक छ जस्तो देखिन्छ।", + "title": "केहि तपाईंको ध्यान आवश्यक छ" + }, + "itemError": "यस सेटिङमा केही गडबड छ। यसलाई ठीक गर्न फेरि सेटअप गर्नुहोस्।", + "items": { + "default": "डिफल्ट सेटअप", + "extension": "एक्सटेन्सन", + "proxy": "आफ्नै प्रोक्सी" + }, + "redoSetup": "पुन: सेटअप गर्नुहोस्", + "successStatus": { + "description": "तपाइँ तपाइँको मनपर्ने मिडिया हेर्न सुरु गर्न को लागी सबै चीजहरू छन्।", + "title": "सबै कुरा मिलाइएको छ!" + }, + "unsetStatus": { + "description": "कृपया सेटअप प्रक्रिया सुरु गर्न दायाँ तिरको बटनमा क्लिक गर्नुहोस्।", + "title": "तपाईं सेटअप मार्फत जानुभएको छैन" + } + }, + "title": "संबन्धहरु", + "workers": { + "addButton": "नया worker हरु हाल्नुहोस", + "description": "एप्लिकेसन प्रकार्य बनाउनको लागि, सबै ट्राफिक प्रोक्सीहरू मार्फत रूट गरिएको छ। यदि तपाईं आफ्नो कामदारहरू ल्याउन चाहनुहुन्छ भने यसलाई सक्षम गर्नुहोस्। <0>निर्देशनहरू।", + "emptyState": "अहिलेसम्म worker हरु छैनन्, तल एउटा थप्नुहोस्", + "label": "आफ्नै proxy workers हरु चलाउनुहोस्", + "urlLabel": "Worker URL हरु", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "एपको भाषा", + "languageDescription": "सम्पूर्ण एप्लिकेसनमा भाषा लागू भयो।", + "thumbnail": "थम्बनेलहरू बनाउनुहोस्", + "thumbnailDescription": "अधिकांश समय, भिडियोहरूमा थम्बनेलहरू हुँदैनन्। तपाईंले वर्तमानमा नै तिनीहरूलाई उत्पन्न गर्न यो सेटिङ सक्षम गर्न सक्नुहुन्छ तर तिनीहरूले तपाईंको भिडियोलाई सुस्त बनाउन सक्छन्।", + "thumbnailLabel": "थम्बनेलहरू बनाउनुहोस्", + "title": "प्राथमिकता" + }, + "reset": "रिसेट गर्नुहोस्", + "save": "सेभ गर्नुहोस्", + "sidebar": { + "info": { + "appVersion": "एप संस्करण", + "backendUrl": "ब्याकइन्ड URL", + "backendVersion": "ब्याकएन्ड संस्करण", + "hostname": "होस्टको नाम", + "insecure": "असुरक्षित", + "notLoggedIn": "तपाईं लग्द इन हुनुहुन्न", + "secure": "सुरक्षित", + "title": "एप बारे जानकारी", + "unknownVersion": "अज्ञात", + "userId": "प्रयोगकर्ता ID" + } + }, + "subtitles": { + "backgroundLabel": "पृष्ठभूमि अस्पष्टता", + "colorLabel": "रङ", + "previewQuote": "म डराउनु हुँदैन। डर दिमागको हत्यारा हो।", + "textSizeLabel": "टेक्स्ट साइज", + "title": "उपशीर्षक" + }, + "unsaved": "तपाईंसँग सुरक्षित नगरिएका परिवर्तनहरू छन्" + } +} diff --git a/src/assets/locales/nl.json b/src/assets/locales/nl.json new file mode 100644 index 00000000..331b1411 --- /dev/null +++ b/src/assets/locales/nl.json @@ -0,0 +1,533 @@ +{ + "about": { + "description": "sudo-flix is een webapplicatie die het internet afzoekt naar streams. Het team streeft naar een minimalistische benadering van het consumeren van inhoud.", + "faqTitle": "Veelgestelde vragen", + "q1": { + "body": "sudo-flix host geen inhoud. Wanneer je op iets klikt om te bekijken, wordt het internet afgezocht naar de geselecteerde media (op het laadscherm en in het tabblad 'videobronnen' kun je zien welke bron je gebruikt). Media wordt nooit geüpload door sudo-flix, alles gaat via dit zoekmechanisme.", + "title": "Waar komt de content vandaan?" + }, + "q2": { + "body": "Het is niet mogelijk om een show of film aan te vragen, sudo-flix beheert geen content. Alle inhoud wordt bekeken via bronnen op het internet.", + "title": "Waar kan ik een show of film aanvragen?" + }, + "q3": { + "body": "Onze zoekresultaten worden aangestuurd door The Movie Database (TMDB) en worden weergegeven ongeacht of onze bronnen de inhoud daadwerkelijk hebben.", + "title": "De zoekresultaten tonen de show of film, waarom kan ik hem niet afspelen?" + }, + "title": "Over sudo-flix" + }, + "actions": { + "copied": "Gekopieerd", + "copy": "Kopieer" + }, + "auth": { + "createAccount": "Heb je nog geen account? <0>Maak er dan een.", + "deviceNameLabel": "Naam toestel", + "deviceNamePlaceholder": "Mijn telefoon", + "generate": { + "description": "Je passphrase werkt als je gebruikersnaam en wachtwoord. Sla je passphrase dus goed op, je hebt hem namelijk nodig om in te loggen", + "next": "Ik heb mijn passphrase opgeslagen", + "passphraseFrameLabel": "Wachtwoord", + "title": "Uw wachtwoord" + }, + "hasAccount": "Heb je al een account? <0>Log hier in.", + "login": { + "description": "Vul je wachtwoord in", + "deviceLengthError": "Vul de naam van je apparaat in", + "passphraseLabel": "12-Woordelijke wachtwoord", + "passphrasePlaceholder": "Wachtwoord", + "submit": "Log in", + "title": "Log in bij je account", + "validationError": "Incorrecte of incompleet wachtwoord" + }, + "register": { + "information": { + "color1": "Profielkleur 1", + "color2": "Profielkleur 2", + "header": "Vul hier het naam van je apparaat. Kies ook je de kleuren die je wil, en een icoontje", + "icon": "Icoontje", + "next": "Volgende", + "title": "Account informatie" + } + }, + "trust": { + "failed": { + "text": "Heb je het goed ingesteld?", + "title": "Kon niet met de server verbinden" + }, + "host": "Je gaat zo verbinden met <0>{{hostname}}, check even of je deze link vertrouwt", + "no": "Vorige pagina", + "title": "Vertrouw je deze server?", + "yes": "Ik vertrouw deze server" + }, + "verify": { + "description": "Vul je wachtwoord in zodat we weten dat je het opgeslagen hebt, dan kunnen we je account maken", + "invalidData": "Ongeldige data", + "noMatch": "Wachtwoord komt niet overeen", + "passphraseLabel": "Jouw wachtwoord", + "recaptchaFailed": "ReCatpcha validatie is mislukt", + "register": "Maak een account", + "title": "Bevestig je wachtwoord" + } + }, + "errors": { + "badge": "Tis kapot", + "details": "Informatie over foutmelding", + "reloadPage": "Herlaad de pagina", + "showError": "Meer informatie over foutmelding", + "title": "Er is iets fout gegaan!" + }, + "footer": { + "legal": { + "disclaimer": "Disclaimer", + "disclaimerText": "sudo-flix host geen bestanden, maar linkt alleen naar diensten van derden. Juridische kwesties moeten worden opgenomen met de bestandshosts en providers. sudo-flix is niet verantwoordelijk voor mediabestanden die worden getoond door de videoproviders." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "Github" + }, + "tagline": "Bekijk je favoriete shows en films met deze open source streaming app." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Over", + "dmca": "DMCA", + "login": "Login", + "onboarding": "Instellen", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registreren", + "settings": "Instellingen" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Opgeslagen" + }, + "continueWatching": { + "sectionTitle": "Kijk verder" + }, + "mediaList": { + "stopEditing": "Stoppen met bewerken" + }, + "search": { + "allResults": "Dat is het!", + "failed": "Het is niet gelukt de media te laden, probeer het nog eens!", + "loading": "Aan het zoeken...", + "noResults": "We konden helaas niets vinden!", + "placeholder": "Wat wil je graag kijken?", + "sectionTitle": "Zoekresultaten" + }, + "titles": { + "day": { + "default": "Wat wil je vanmiddag kijken?", + "extra": [ + "Voel je je avontuurlijk? Dan is Jurassic Park misschien wel de perfecte keuze." + ] + }, + "morning": { + "default": "Waar wil je deze ochtend naar kijken?", + "extra": ["Ik hoor dat Before Sunrise goed is"] + }, + "night": { + "default": "Wat wil je vanavond bekijken?", + "extra": ["Moe? Ik hoor dat The Exorcist goed is."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} A{{episode}}", + "types": { + "movie": "Film", + "show": "Serie" + } + }, + "navigation": { + "banner": { + "offline": "Controleer je internetverbinding" + }, + "menu": { + "about": "Over ons", + "donation": "Doneer", + "logout": "Log uit", + "register": "Synchroniseren met de cloud", + "settings": "Instellingen", + "support": "Ondersteuning" + } + }, + "notFound": { + "badge": "Pagina niet gevonden", + "goHome": "Naar de home-pagina", + "message": "We hebben echt alles geprobeerd, zelfs tijdrijzen; echter hebben we deze pagina helaas niet kunnen vinden.", + "title": "Pagina niet gevonden" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Annuleren", + "confirm": "Gebruik standaardinstallatie", + "description": "De standaardinstallatie heeft niet de beste streams en kan onbruikbaar traag zijn.", + "title": "Weet je het zeker?" + }, + "extension": { + "back": "Terug", + "explainer": "Door gebruik te maken van de browserextensie kun je de beste streams krijgen. Met slechts een eenvoudige installatie.", + "explainerIos": "Helaas, de browserextensie is niet ondersteund op iOS. Druk op Terug om een andere optie te kiezen.", + "extensionHelp": "Als je de extensie hebt geïnstalleerd maar niet wordt gedetecteerd, Open dan de extensie via het extensies menu in je browser en volg de stappen op het scherm.", + "linkChrome": "Installeer de Chrome-extensie", + "linkFirefox": "Installeer de Firefox-extensie", + "notDetecting": "Geïnstalleerd op Chrome maar wordt niet weergegeven? Probeer de pagina opnieuw te laden!", + "notDetectingAction": "Pagina opnieuw laden", + "status": { + "disallowed": "Extensie is niet ingeschakeld voor deze pagina", + "disallowedAction": "Extensie inschakelen", + "failed": "Mislukt om status aan te vragen", + "loading": "Aan het wachten tot je de extensie hebt geïnstalleerd", + "outdated": "Extensieversie te oud", + "success": "Extensie werkt zoals verwacht!" + }, + "submit": "Doorgaan", + "title": "Laten we beginnen met de extensie" + }, + "proxy": { + "back": "Terug", + "explainer": "Met de proxy-methode kun je hoogwaardige streams verkrijgen door zelf een proxy op te zetten.", + "input": { + "errorConnection": "Kon geen verbinding maken met de proxy", + "errorInvalidUrl": "Geen geldige URL", + "errorNotProxy": "Verwachtte een proxy maar kreeg een website", + "label": "Proxy URL", + "placeholder": "https://" + }, + "link": "Leer hoe je de proxy kunt instellen", + "submit": "Proxy opslaan", + "title": "Laten we een nieuwe proxy instellen" + }, + "start": { + "explainer": "Om de beste mogelijke streams te krijgen, moet je kiezen welke streamingmethode je wilt gebruiken.", + "options": { + "default": { + "text": "Ik wil geen streams van goede kwaliteit, <0 /> <1>Gebruik de standaardinstellingen." + }, + "extension": { + "action": "Installeer extensie", + "description": "Installeer browserextensie en krijg toegang tot de beste bronnen.", + "quality": "Beste kwaliteit", + "title": "Browserextensie" + }, + "proxy": { + "action": "Proxy instellen", + "description": "Proxy instellen in slechts 5 minuten en krijg toegang tot geweldige bronnen.", + "quality": "Goede kwaliteit", + "title": "Eigen proxy" + } + }, + "title": "Laten we beginnen met het instellen van sudo-flix" + } + }, + "overlays": { + "close": "Sluiten" + }, + "player": { + "back": { + "default": "Naar de home-pagina", + "short": "Terug" + }, + "casting": { + "enabled": "Aan het casten..." + }, + "menus": { + "downloads": { + "disclaimer": "Downloads worden direct bij de bron opgehaald. sudo-flix heeft geen controle over het bestand dat je ontvangt.", + "downloadPlaylist": "Afspeellijst downloaden", + "downloadSubtitle": "Download huidige ondertiteling", + "downloadVideo": "Download filmpje", + "hlsDisclaimer": "Downloads worden rechtstreeks van de aanbieder gehaald. sudo-flix heeft geen controle over de manier waarop de downloads worden aangeboden.

Houd er rekening mee dat u nu een HLS-afspeellijst downloadt, het wordt afgeraden deze te downloaden als u niet bekend bent met geavanceerde streamingbestandstypen. Probeer verschillende bronnen voor verschillende streamingbestandstypen.", + "onAndroid": { + "1": "Om te downloaden op Android, klik je op de downloadknop en vervolgens, op de nieuwe pagina, tap and hold op de video en selecteer save.", + "shortTitle": "Download / Android", + "title": "Downloaden op Android" + }, + "onIos": { + "1": "Als je wilt downloaden op iOS, klik je op de downloadknop en vervolgens op de nieuwe pagina op en vervolgens op Save to Files .", + "shortTitle": "Download / iOS", + "title": "Downloaden op iOS" + }, + "onPc": { + "1": "Klik op de downloadknop op de pc en klik op de nieuwe pagina met de rechtermuisknop op de video en selecteer Video opslaan als", + "shortTitle": "Download / PC", + "title": "Downloaden op PC" + }, + "title": "Download" + }, + "episodes": { + "button": "Afleveringen", + "emptyState": "Er zijn in dit seizoen geen afleveringen, kijk over een paar jaar nog eens!", + "episodeBadge": "A{{episode}}", + "loadingError": "Er ging iets mis bij het laden van dit seizoen", + "loadingList": "Aan het laden...", + "loadingTitle": "Aan het zoeken...", + "unairedEpisodes": "Een of meer afleveringen van dit seizoen zijn uitgeschakeld omdat ze nog niet zijn uitgezonden." + }, + "playback": { + "speedLabel": "Afspeelsnelheid", + "title": "Afspeel instellingen" + }, + "quality": { + "automaticLabel": "Automatische kwaliteit", + "hint": "Je kunt <0>bron omschakelen proberen om verschillende kwaliteitsopties te krijgen.", + "iosNoQuality": "Vanwege door Apple gedefinieerde beperkingen is kwaliteitsselectie niet beschikbaar op iOS voor deze bron. U kunt <0>omschakelen naar een andere bron proberen om andere kwaliteitsopties te krijgen.", + "title": "Kwaliteit" + }, + "settings": { + "downloadItem": "Download", + "enableSubtitles": "Ondertitels inschakelen", + "experienceSection": "Kijk-ervaring", + "playbackItem": "Afspeel instellingen", + "qualityItem": "Kwaliteit", + "sourceItem": "Video-bron", + "subtitleItem": "Instellingen ondertiteling", + "videoSection": "Video instellingen" + }, + "sources": { + "failed": { + "text": "Er ging iets mis bij het zoeken naar videos, probeer een andere bron.", + "title": "Het is niet gelukt dit op te halen" + }, + "noEmbeds": { + "text": "We konden geen embeds vinden, probeer een andere bron.", + "title": "Geen embeds gevonden" + }, + "noStream": { + "text": "Deze bron heeft geen links voor deze film of serie.", + "title": "Geen bron" + }, + "title": "Bronnen", + "unknownOption": "Onbekend" + }, + "subtitles": { + "customChoice": "Selecteer ondertiteling uit bestand", + "customizeLabel": "Aanpassen", + "offChoice": "Uit", + "settings": { + "backlink": "Aangepaste ondertiteling", + "delay": "Ondertiteling vertraging", + "fixCapitals": "Hoofdlettergebruik corrigeren" + }, + "title": "Ondertiteling", + "unknownLanguage": "Onbekend" + } + }, + "metadata": { + "api": { + "text": "Kan API-metagegevens niet laden. Controleer uw internetverbinding.", + "title": "Kan API-metagegevens niet laden" + }, + "dmca": { + "badge": "Weggehaald", + "text": "Deze media is niet langer beschikbaar vanwege een verwijderingsverzoek of auteursrechtenclaim.", + "title": "Media is weggehaald" + }, + "extensionPermission": { + "badge": "Toestemming ontbreekt", + "button": "Gebruik de extensie", + "text": "Je hebt de browserextensie, maar we hebben jouw toestemming nodig om gebruik te maken van de extensie.", + "title": "Configureer de extensie" + }, + "failed": { + "badge": "Mislukt", + "homeButton": "Ga naar de home-pagina", + "text": "We konden geen informatie over deze media ophalen bij TMDB. Kijk even na of TMDB te bereiken is op dit netwerk.", + "title": "Metadata ophalen mislukt" + }, + "notFound": { + "badge": "Pagina niet gevonden", + "homeButton": "Naar de home-pagina", + "text": "We konden dit stukje media niet vinden. Het is mogelijk verwijderd, of jij hebt zelf de URL aangepast.", + "title": "We konden deze media niet vinden." + } + }, + "nextEpisode": { + "cancel": "Annuleren", + "next": "Volgende aflevering" + }, + "playbackError": { + "badge": "Afspeelfout", + "errors": { + "errorAborted": "Het laden van de media is stopgezet omdat de gebruiker daar om vroeg.", + "errorDecode": "Ondanks het feit dat we eerder dachten dat deze bron beschikbaar, was dat toch niet zo en is er een decode error ontstaan.", + "errorGenericMedia": "Onbekende media foutmelding.", + "errorNetwork": "Er ging iets mis waardoor de media niet langer beschikbaar is, al was hij dat eerst wel.", + "errorNotSupported": "Deze media or media provider wordt niet ondersteund." + }, + "homeButton": "Naar de home-pagina", + "text": "Er ging iets mis bij het afspelen. Probeer het nog eens.", + "title": "Oeps, hier ging iets mis!" + }, + "scraping": { + "items": { + "failure": "Daar ging iets mis", + "notFound": "Er is geen video gevonden", + "pending": "Naar videos aan het zoeken..." + }, + "notFound": { + "badge": "Niet gevonden", + "detailsButton": "Meer informatie", + "homeButton": "Naar de home-pagina", + "text": "We hebben al onze providers lief aangekeken maar we hebben niets kunnen vinden. Wij beheersen de media niet, en bepalen dus niet wat er beschikbaar is. Klik op \"meer informatie over foutmelding\" voor meer informatie.", + "title": "Dat konden we niet vinden" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "Nog {{timeLeft}} • Klaar om {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Bevestig dat u een mens bent door de Captcha aan de rechterkant in te vullen. Dit is om sudo-flix veilig te houden!", + "error": "Kan uw menselijkheid niet verifiëren. Probeer het opnieuw.", + "title": "We moeten verifiëren dat u een mens bent.", + "verifyingHumanity": "Je menselijkheid verifiëren..." + } + }, + "screens": { + "dmca": { + "text": "Welkom op de DMCA-contactpagina van sudo-flix! Wij respecteren intellectuele eigendomsrechten en willen eventuele problemen met auteursrechten snel aanpakken. Als u van mening bent dat uw auteursrechtelijk beschermde werk onjuist is gebruikt op ons platform, stuur dan een gedetailleerde DMCA-kennisgeving naar het onderstaande e-mailadres. Vermeld een beschrijving van het auteursrechtelijk beschermde materiaal, uw contactgegevens en een verklaring dat u te goeder trouw bent. Wij zetten ons in om deze zaken snel op te lossen en waarderen uw medewerking om sudo-flix een plek te houden waar creativiteit en auteursrechten worden gerespecteerd.", + "title": "DMCA" + }, + "loadingApp": "Toepassing laden", + "loadingUser": "Je profiel laden", + "loadingUserError": { + "logout": "Uitloggen", + "reset": "Aangepaste server resetten", + "text": "Het laden van uw profiel is mislukt", + "textWithReset": "Is het laden van je profiel van je aangepaste server mislukt en wil je terug naar de standaardserver?" + }, + "migration": { + "failed": "Het migreren van uw gegevens is mislukt.", + "inProgress": "Even geduld, we zijn je gegevens aan het migreren. Dit zal niet lang duren." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Apparaatnaam", + "deviceNamePlaceholder": "Persoonlijke telefoon", + "editProfile": "Bewerken", + "logoutButton": "Uitloggen" + }, + "actions": { + "delete": { + "button": "Account verwijderen", + "confirmButton": "Account verwijderen", + "confirmDescription": "Weet je zeker dat je je account wilt verwijderen? Al je gegevens zullen verloren gaan!", + "confirmTitle": "Weet je het zeker?", + "text": "Deze actie is onomkeerbaar. Alle gegevens worden verwijderd en er kan niets worden hersteld.", + "title": "Account verwijderen" + }, + "title": "Acties" + }, + "devices": { + "deviceNameLabel": "Apparaatnaam", + "failed": "Sessies niet geladen", + "removeDevice": "Verwijder", + "title": "Apparaten" + }, + "profile": { + "finish": "Bewerken voltooien", + "firstColor": "Profielkleur 1", + "secondColor": "Profielkleur 2", + "title": "Profielfoto bewerken", + "userIcon": "Gebruikerspictogram" + }, + "register": { + "cta": "Aan de slag", + "text": "Deel uw kijkvoortgang tussen apparaten en houd ze gesynchroniseerd.", + "title": "Synchroniseren met de cloud" + }, + "title": "Account" + }, + "appearance": { + "activeTheme": "Actief", + "themes": { + "blue": "Blauw", + "default": "Standaard", + "gray": "Grijs", + "red": "Rood", + "teal": "Groenblauw" + }, + "title": "Uiterlijk" + }, + "connections": { + "server": { + "description": "Als je verbinding wilt maken met een eigen backend om je gegevens op te slaan, schakel dan deze optie in en geef de URL op. <0>Instructies.", + "label": "Eigen server", + "urlLabel": "Eigen server URL" + }, + "setup": { + "doSetup": "Instellen", + "errorStatus": { + "description": "Het lijkt erop dat één of meer items in deze configuratie jouw aandacht nodig hebben.", + "title": "Iets vereist jouw aandacht" + }, + "itemError": "Er is iets mis met deze instelling. Klik op opnieuw instellen om op te lossen.", + "items": { + "default": "Standaard installatie", + "extension": "Extensie", + "proxy": "Eigen proxy" + }, + "redoSetup": "Opnieuw instellen", + "successStatus": { + "description": "In alles is ingesteld, je kunt nu je favoriete media bekijken.", + "title": "Alles is ingesteld!" + }, + "unsetStatus": { + "description": "Klik op de knop aan de rechterkant om het installatieproces te starten.", + "title": "Je hebt de installatieprocedure nog niet doorlopen" + } + }, + "title": "Verbindingen", + "workers": { + "addButton": "Nieuwe worker toevoegen", + "description": "Om de applicatie te laten werken, wordt al het verkeer omgeleid via proxies. Schakel dit in als je je eigen workers wilt gebruiken. <0>Instructies.", + "emptyState": "Nog geen workers, voeg er hieronder een toe", + "label": "Eigen proxy werker gebruiken", + "urlLabel": "Worker URLs", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Applicatietaal", + "languageDescription": "Taal toegepast op de gehele applicatie.", + "thumbnail": "Genereer miniaturen", + "thumbnailDescription": "Meestal hebben video's geen miniaturen. Je kunt deze instelling inschakelen om ze dynamisch te genereren, maar dit kan je video vertragen.", + "thumbnailLabel": "Genereer miniaturen", + "title": "Voorkeuren" + }, + "reset": "Resetten", + "save": "Wijzigingen opslaan", + "sidebar": { + "info": { + "appVersion": "App versie", + "backendUrl": "Backend URL", + "backendVersion": "Backend versie", + "hostname": "hostnaam", + "insecure": "Onveilig", + "notLoggedIn": "U bent niet ingelogd", + "secure": "Veilig", + "title": "App informatie", + "unknownVersion": "Onbekend", + "userId": "Gebruiker ID" + } + }, + "subtitles": { + "backgroundLabel": "Achtergrond dekking", + "colorLabel": "Kleur", + "previewQuote": "Ik mag niet bang zijn. Angst doodt de geest.", + "textSizeLabel": "Tekengrootte", + "title": "Ondertiteling" + }, + "unsaved": "U heeft niet-opgeslagen wijzigingen" + } +} diff --git a/src/assets/locales/nv.json b/src/assets/locales/nv.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/assets/locales/nv.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/assets/locales/pa.json b/src/assets/locales/pa.json new file mode 100644 index 00000000..1214e217 --- /dev/null +++ b/src/assets/locales/pa.json @@ -0,0 +1,64 @@ +{ + "about": { + "description": "ਮੂਵੀ-ਵੈਬ ਇੱਕ ਵੈੱਬ ਐਪਲੀਕੇਸ਼ਨ ਹੈ ਜੋ ਸਟ੍ਰੀਮ ਲਈ ਇੰਟਰਨੈਟ ਦੀ ਖੋਜ ਕਰਦੀ ਹੈ. ਟੀਮ ਦਾ ਉਦੇਸ਼ ਕੰਟੈਂਟ ਦੀ ਖਪਤ ਕਰਨ ਲਈ ਜ਼ਿਆਦਾਤਰ ਘੱਟੋ-ਘੱਟ ਪਹੁੰਚ ਹੈ.", + "faqTitle": "ਆਮ ਸਵਾਲ", + "q1": { + "body": "ਮੂਵੀ ਵੈੱਬ ਕਿਸੇ ਕੰਟੈਂਟ ਦੀ ਮੇਜ਼ਬਾਨੀ ਨਹੀਂ ਕਰਦਾ ਹੈ.ਜਦੋਂ ਤੁਸੀਂ ਦੇਖਣ ਲਈ ਕਿਸੇ ਚੀਜ਼ 'ਤੇ ਕਲਿੱਕ ਕਰਦੇ ਹੋ, ਤਾਂ ਚੁਣੇ ਹੋਏ ਮੀਡੀਆ ਲਈ ਇੰਟਰਨੈੱਟ ਖੋਜਿਆ ਜਾਂਦਾ ਹੈ (ਲੋਡਿੰਗ ਸਕ੍ਰੀਨ 'ਤੇ ਅਤੇ 'ਵੀਡੀਓ ਸਰੋਤ' ਟੈਬ ਵਿੱਚ ਤੁਸੀਂ ਦੇਖ ਸਕਦੇ ਹੋ ਕਿ ਤੁਸੀਂ ਕਿਹੜਾ ਸਰੋਤ ਵਰਤ ਰਹੇ ਹੋ) ਮੀਡੀਆ ਕਦੇ ਵੀ ਮੂਵੀ-ਵੈੱਬ ਦੁਆਰਾ ਅਪਲੋਡ ਨਹੀਂ ਹੁੰਦਾ, ਸਭ ਕੁਝ ਇਸ ਖੋਜ ਵਿਧੀ ਦੁਆਰਾ ਹੁੰਦਾ ਹੈ।", + "title": "ਕੰਟੈਂਟ ਕਿੱਥੋਂ ਆਉਂਦੀ ਹੈ?" + }, + "q2": { + "body": "ਕਿਸੇ ਸ਼ੋਅ ਜਾਂ ਮੂਵੀ ਦੀ ਬੇਨਤੀ ਕਰਨਾ ਸੰਭਵ ਨਹੀਂ ਹੈ, ਮੂਵੀ-ਵੈੱਬ ਕਿਸੇ ਵੀ ਕੰਟੈਂਟ ਦਾ ਪ੍ਰਬੰਧਨ ਨਹੀਂ ਕਰਦਾ ਹੈ। ਸਾਰੇ ਕੰਟੈਂਟ ਨੂੰ ਇੰਟਰਨੈੱਟ 'ਤੇ ਸਰੋਤਾਂ ਰਾਹੀਂ ਦੇਖਿਆ ਜਾਂਦਾ ਹੈ।", + "title": "ਮੈਂ ਇੱਕ ਸ਼ੋਅ ਜਾਂ ਫ਼ਿਲਮ ਲਈ ਕਿੱਥੇ ਬੇਨਤੀ ਕਰ ਸਕਦਾ/ਸਕਦੀ ਹਾਂ?" + }, + "q3": { + "title": "ਖੋਜ ਨਤੀਜੇ ਸ਼ੋਅ ਜਾਂ ਫ਼ਿਲਮ ਦਿਖਾਉਂਦੇ ਹਨ, ਮੈਂ ਇਸਨੂੰ ਕਿਉਂ ਨਹੀਂ ਚਲਾ ਸਕਦਾ?" + }, + "title": "ਮੂਵੀ-ਵੈੱਬ ਡੀ ਬਾਰੇ" + }, + "actions": { + "copied": "ਕਾਪੀਡ", + "copy": "ਕਾਪੀ" + }, + "auth": { + "deviceNameLabel": "ਡਿਵਾਈਸ ਦਾ ਨਾਮ", + "generate": { + "passphraseFrameLabel": "ਪੈਰਾਫਰਾਜ਼" + }, + "login": { + "passphrasePlaceholder": "ਪੈਰਾਫਰਾਜ਼", + "submit": "ਲੌਗ-ਇਨ" + }, + "register": { + "information": { + "next": "ਅਗਲਾ" + } + }, + "trust": { + "no": "ਵਾਪਸ ਜਾਓ", + "yes": "ਮੈਨੂੰ ਇਸ ਸਰਵਰ 'ਤੇ ਭਰੋਸਾ ਹੈ" + } + }, + "footer": { + "links": { + "discord": "ਡਿਸਕੋਰਡ", + "dmca": "ਡੀ.ਐਮ.ਸੀ.ਏ", + "github": "ਗਿਥਬ" + } + }, + "global": { + "name": "ਮੂਵੀ-ਵੈੱਬ", + "pages": { + "about": "ਬਾਰੇ", + "dmca": "ਡੀ.ਐਮ.ਸੀ.ਏ", + "login": "ਲਾਗਿਨ", + "pagetitle": "{{title}} - ਮੂਵੀ-ਵੈੱਬ", + "register": "ਰਜਿਸਟਰ", + "settings": "ਸੈਟਿੰਗਾਂ" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "ਬੁੱਕਮਾਰਕ" + } + } +} diff --git a/src/assets/locales/pirate.json b/src/assets/locales/pirate.json new file mode 100644 index 00000000..8c8efb5d --- /dev/null +++ b/src/assets/locales/pirate.json @@ -0,0 +1,396 @@ +{ + "about": { + "description": "Ahoy there! \"sudo-flix\" be a fine vessel on the digital seas, a web application scourin' the vast internet for streams. The crew be settin' their sights on a mostly minimalistic approach to enjoyin' the booty of entertainment. Arrr!", + "faqTitle": "Common queries", + "q1": { + "body": "Arrr! \"sudo-flix\" be a noble ship that don't harbor any content itself. When ye click to set yer eyes on a treasure, the application sets sail across the internet, scourin' the vast seas for the chosen media. Check the loading screen and the 'video sources' tab to spy on which source be providin' the entertainment. No media be uploaded by \"sudo-flix\" — it be all through this searchin' mechanism, savvy?", + "title": "From which cove does the content emerge, me heartie?" + }, + "q2": { + "body": "Avast, ye landlubber! Be warned, ye can't be requestin' a show or movie on \"sudo-flix,\" for it don't manage any content. All the booty ye set yer eyes on be viewed through sources sailin' the internet. The ship merely be a vessel, not a keeper of the treasures. Navigate wisely, me heartie!", + "title": "Where be I able to make a hearty request for a show or movie?" + }, + "q3": { + "body": "Arrr, set yer sights on this, me matey! Our search results be fueled by The Movie Database (TMDB), showin' the way regardless of whether our sources in the vast digital seas actually bear the coveted content. It be a guide, not a promise of a sure find. Navigate the waters wisely, for the winds of the internet can be fickle!", + "title": "Why be the search results unveilin' the show or movie, yet I can't set sail to watch it?" + }, + "title": "The tales of \"sudo-flix\"" + }, + "actions": { + "copied": "Copied", + "copy": "Copy" + }, + "auth": { + "createAccount": "Don't have a ship yet? <0>Hoist the sails and make yer own", + "deviceNameLabel": "Ship name", + "deviceNamePlaceholder": "Muad'Dib's Pirate Ship", + "generate": { + "description": "If ye lose this, ye be a silly goose and will be posted on the wall of shame™️", + "next": "Ahoy! I've stashed me secret code away for safe keepin'.", + "passphraseFrameLabel": "Secret Code", + "title": "Yer Passphrase" + }, + "hasAccount": "Arrr! Ye already got a ship on the seas? <0>Board here, me heartie!", + "login": { + "description": "Arr, ye be askin' for the key to me top-secret lair, also known as The Fortress of Wordsmithery, accessed only by recitin' the sacred incantation of the 12-word passphrase!", + "deviceLengthError": "Bestow a name upon yer contraption, me heartie!", + "passphraseLabel": "12-Word Passphrase", + "passphrasePlaceholder": "Passphrase", + "submit": "Hoist Anchor", + "title": "Hoist the Jolly Roger", + "validationError": "Arr, incorrect or incomplete passphrase" + }, + "register": { + "information": { + "color1": "First Mate color", + "color2": "Second Mate color", + "header": "Enter a moniker for yer ship and choose a pirate icon and colors, arrr!", + "icon": "Pirate icon", + "next": "Forward", + "title": "Pirate Account information" + } + }, + "trust": { + "failed": { + "text": "Did ye configure it correctly?", + "title": "Failed to reach the backend" + }, + "host": "Do ye trust <0>{{hostname}}?", + "no": "Abandon Ship", + "title": "Do ye trust this ship?", + "yes": "Trust" + }, + "verify": { + "description": "If ye already lost it, how will ye ever be able to take care of a wee buccaneer?", + "invalidData": "Data be not valid", + "noMatch": "Passphrase doesn't match", + "passphraseLabel": "Yer passphrase", + "recaptchaFailed": "ReCaptcha validation failed", + "register": "Forge yer account, matey!", + "title": "Enter yer passphrase" + } + }, + "errors": { + "badge": "Shiver me timbers", + "details": "Ahoy! What be the secrets of the glitch?", + "reloadPage": "Reload the page", + "showError": "Reveal the details of the error, me matey", + "title": "That be an error, Captain" + }, + "footer": { + "legal": { + "disclaimer": "Disclaimer", + "disclaimerText": "sudo-flix does not host any files, it merely links to 3rd party services. Legal issues should be taken up with the file hosts and providers. sudo-flix be not responsible for any media files shown by the video providers." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Watch yer favorite shows and movies with this open source streaming ship." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "About", + "dmca": "DMCA", + "login": "Login", + "pagetitle": "{{title}} - sudo-flix", + "register": "Forge yer account, matey!", + "settings": "Settings" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Bookmarks" + }, + "continueWatching": { + "sectionTitle": "Continue Watchin'" + }, + "mediaList": { + "stopEditing": "Stop editin'" + }, + "search": { + "allResults": "That's all we have, me heartie!", + "failed": "Arrrr failed to find media, try again!", + "loading": "Hold yer horses, me heartie!", + "noResults": "We couldn't find anythin', arrr!", + "placeholder": "What do ye want to watch?", + "sectionTitle": "Searchin' results" + }, + "titles": { + "day": { + "default": "What be yer fancy for this fine afternoon's viewing, me heartie?" + }, + "morning": { + "default": "What be yer fancy for this mornin's viewing, me heartie?", + "extra": [ + "I be hearin' that 'Before Sunrise' be a fine choice, me heartie" + ] + }, + "night": { + "default": "What be yer desire for the night's entertainment, me heartie?", + "extra": [ + "Feelin' weary, are ye? I be hearin' 'The Exorcist' be a good pick to stir the spirits, me heartie." + ] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Show" + } + }, + "navigation": { + "banner": { + "offline": "Check yer internet connection" + }, + "menu": { + "about": "About us", + "donation": "Share yer treasures and donate to the cause", + "logout": "Abandon ship", + "register": "Sync to the cloud", + "settings": "Settings", + "support": "Support" + } + }, + "notFound": { + "badge": "Not found", + "goHome": "Back to home port", + "message": "We looked everywhere: under the bins, in the closet, behind the proxy but ultimately couldn't find the treasure map ye be lookin' for.", + "title": "Couldn't find that treasure map" + }, + "overlays": { + "close": "Close" + }, + "player": { + "back": { + "default": "Back to home port", + "short": "Back" + }, + "casting": { + "enabled": "Casting to the contraption, setting sail..." + }, + "menus": { + "downloads": { + "disclaimer": "Downloads be taken directly from the provider. sudo-flix does not have control over how the downloads be provided.", + "downloadVideo": "Download film", + "onAndroid": { + "1": "To download on Android, tap and hold on the film, then select save.", + "shortTitle": "Download / Android", + "title": "Downloadin' on Android" + }, + "onIos": { + "1": "To download on iOS, click , then Save to Files . All that's left to do now be to pick a nice and cozy chest for yer film!", + "shortTitle": "Download / iOS", + "title": "Downloadin' on iOS" + }, + "onPc": { + "1": "On PC, right click the film and select Save film as", + "shortTitle": "Download / PC", + "title": "Downloadin' on PC" + }, + "title": "Buried Treasure" + }, + "episodes": { + "button": "Episodes", + "emptyState": "There be no episodes in this season, check back later!", + "episodeBadge": "E{{episode}}", + "loadingError": "Error loadin' season", + "loadingList": "Loading...", + "loadingTitle": "Loading..." + }, + "playback": { + "speedLabel": "Playback speed", + "title": "Playback settings" + }, + "quality": { + "automaticLabel": "Automatic quality", + "hint": "Ye can try <0>switchin' source to get different quality options.", + "iosNoQuality": "Due to Apple-defined limitations, quality selection be not available on iOS for this source. Ye can try <0>switchin' to another source to get different quality options.", + "title": "Quality" + }, + "settings": { + "downloadItem": "Buried Treasure", + "enableSubtitles": "Unfurl the subtitles", + "experienceSection": "Viewing Experience", + "playbackItem": "Playback settings", + "qualityItem": "Quality", + "sourceItem": "Video sources", + "subtitleItem": "Set yer subtitle course", + "videoSection": "Video settings" + }, + "sources": { + "failed": { + "text": "We were unable to find any videos for this source. Don't come bitchin' to us about it, just try another source.", + "title": "Failed to scrape" + }, + "noEmbeds": { + "text": "We were unable to find any embeds for this source, please try another.", + "title": "No embeds found" + }, + "noStream": { + "text": "This source has no streams for this film or show.", + "title": "No stream" + }, + "title": "Sources", + "unknownOption": "Unknown" + }, + "subtitles": { + "customizeLabel": "Tailor it to yer liking", + "settings": { + "fixCapitals": "Mend the capital letters" + } + } + }, + "metadata": { + "failed": { + "badge": "Failed", + "homeButton": "Go home port", + "text": "Oh, me apologies, sweetie! The itty-bitty sudo-flix did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle sudo-flix ish twying so hard. Can ye find it in yer heart to forgive? UwU 💖", + "title": "Failed to load meta data" + }, + "notFound": { + "badge": "Not found", + "homeButton": "Back to home port", + "text": "We couldn't find the media ye requested. Either it's been removed or ye tampered with the URL.", + "title": "Couldn't find that media." + } + }, + "nextEpisode": { + "cancel": "Cancel", + "next": "Next episode" + }, + "playbackError": { + "badge": "Not found", + "errors": { + "errorAborted": "The fetchin' of the associated resource was aborted by the user's request.", + "errorDecode": "Despite havin' previously been determined to be usable, an error occurred while tryin' to decode the media resource, resultin' in an error.", + "errorGenericMedia": "Unknown media error occurred", + "errorNetwork": "Some kind of network error occurred which prevented the media from bein' successfully fetched, despite havin' previously been available.", + "errorNotSupported": "The associated resource or media provider object has been found to be unsuitable." + }, + "homeButton": "Go home port", + "text": "Oh, me apologies, sweetie! The itty-bitty sudo-flix did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle sudo-flix ish twying so hard. Can ye find it in yer heart to forgive? UwU 💖", + "title": "Whoops, it broke!" + }, + "scraping": { + "items": { + "failure": "Error occurred", + "notFound": "Doesn't have the video", + "pending": "Checkin' for videos..." + }, + "notFound": { + "badge": "Not found", + "homeButton": "Go home port", + "text": "Oh, me apologies, sweetie! The itty-bitty sudo-flix did its utmost bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`) Please don't be angwy, wittle sudo-flix ish twying so hard. Can ye find it in yer heart to forgive? UwU 💖", + "title": "Goo goo gaa gaa" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} left • Finish at {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + } + }, + "screens": { + "loadingApp": "Loadin' application", + "loadingUser": "Loadin' yer pirate profile", + "loadingUserError": { + "reset": "Reset custom ship", + "text": "Failed to load yer pirate profile", + "textWithReset": "Failed to load yer pirate profile from yer custom ship, want to reset back to default?" + }, + "migration": { + "failed": "Failed to migrate yer booty.", + "inProgress": "Please hold, we be migratin' yer booty. This shouldn't take long." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Ship name", + "deviceNamePlaceholder": "Fremen tablet", + "editProfile": "Edit", + "logoutButton": "Abandon ship" + }, + "actions": { + "delete": { + "button": "Abandon ship for all eternity", + "confirmButton": "Abandon ship for all eternity", + "confirmDescription": "Arrr ye sure ye want to abandon yer ship for all eternity? All yer booty will be lost!", + "confirmTitle": "Arrr ye sure?", + "text": "This action be irreversible. All booty will be deleted and nothin' can be recovered.", + "title": "Abandon ship for all eternity" + }, + "title": "Actions" + }, + "devices": { + "deviceNameLabel": "Ship name", + "failed": "Failed to load sessions", + "removeDevice": "Abandon ship", + "title": "Shipmates" + }, + "profile": { + "finish": "Finish editing", + "firstColor": "First color", + "secondColor": "Second color", + "title": "Edit Pirate Portrait", + "userIcon": "Pirate icon" + }, + "register": { + "cta": "Get started", + "text": "Instantly share yer watch progress between devices and keep 'em synced.", + "title": "Sync to the Cloud" + }, + "title": "Treasure Chest" + }, + "appearance": { + "activeTheme": "Active", + "themes": { + "blue": "Blue", + "default": "Default", + "gray": "Gray", + "red": "Red", + "teal": "Teal" + }, + "title": "Appearance" + }, + "connections": { + "server": { + "description": "To make the application function, all traffic be routed through proxies. Enable this if ye want to bring yer own sailors.", + "label": "Custom ship", + "urlLabel": "Custom ship URL" + }, + "title": "Connections", + "workers": { + "addButton": "Recruit new sailor", + "description": "To make the application function, all traffic be routed through proxies. Enable this if ye want to bring yer own sailors.", + "emptyState": "No sailors yet, add one below", + "label": "Use custom proxy sailors", + "urlLabel": "Sailor URLs", + "urlPlaceholder": "https://" + } + }, + "reset": "Reset", + "save": "Save", + "sidebar": { + "info": { + "appVersion": "App version", + "backendUrl": "Backend URL", + "backendVersion": "Backend version", + "hostname": "Ship name", + "insecure": "Insecure", + "notLoggedIn": "Not logged in", + "secure": "Secure", + "title": "App information", + "unknownVersion": "Unknown", + "userId": "Pirate ID" + } + }, + "unsaved": "Ye have unsaved changes" + } +} diff --git a/src/assets/locales/pl.json b/src/assets/locales/pl.json new file mode 100644 index 00000000..e1b3d9ed --- /dev/null +++ b/src/assets/locales/pl.json @@ -0,0 +1,533 @@ +{ + "about": { + "description": "sudo-flix jest aplikacją internetową, która wyszukuje w internecie strumienia. Zespół dąży do minimalistycznego podejścia do konsumpcji treści.", + "faqTitle": "Częste pytania", + "q1": { + "body": "sudo-flix nie udostępnia żadnych treści. Kiedy klikniesz na coś co chcesz obejrzeć, internet zostanie przeszukany pod kątem wybranych multimediów (na ekranie ładowania oraz w zakładce „źródła wideo” możesz zobaczyć, z jakiego źródła korzystasz). Media nigdy nie są przesyłane przez sudo-flix, wszystko odbywa się za pośrednictwem tego mechanizmu wyszukiwania.", + "title": "Skąd pochodzą treści?" + }, + "q2": { + "body": "Nie można złożyć wniosku o serialu ani filmu, sudo-flix nie zarządza żadną treścią. Wszystkie treści są przeglądane za pośrednictwem źródeł w Internecie.", + "title": "Gdzie mogę złożyć wniosek o seriale lub filmie?" + }, + "q3": { + "body": "Nasze wyniki wyszukiwania pochodzą z TMDB i są wyświetlane niezależnie od tego, czy nasze źródła faktycznie posiadają daną treść.", + "title": "W wyniku wyszukiwania wyświetlany jest serial lub film. Dlaczego nie mogę go odtworzyć?" + }, + "title": "O sudo-flix" + }, + "actions": { + "copied": "Skopiowano", + "copy": "Kopiuj" + }, + "auth": { + "createAccount": "Nie masz jeszcze konta? <0>Utwórz konto.", + "deviceNameLabel": "Nazwa urządzenia", + "deviceNamePlaceholder": "Telefon osobisty", + "generate": { + "description": "Twoja fraza hasła działa jak nazwa użytkownika i hasło. Pamiętaj aby zachować go w bezpiecznym miejscu, ponieważ będziesz musiał go wprowadzić aby zalogować się na swoje konto", + "next": "Zapisałem frazę hasła", + "passphraseFrameLabel": "Fraza hasła", + "title": "Twoja fraza hasła" + }, + "hasAccount": "Masz już konto? <0>Zaloguj się tutaj.", + "login": { + "description": "Proszę wpisać frazę hasła, aby zalogować się na swoje konto", + "deviceLengthError": "Proszę wpisać nazwę urządzenia", + "passphraseLabel": "12-wyrazowa fraza hasła", + "passphrasePlaceholder": "Fraza hasła", + "submit": "Zaloguj się", + "title": "Zaloguj się na swoje konto", + "validationError": "Nieprawidłowe lub niedokończone fraza hasła" + }, + "register": { + "information": { + "color1": "Kolor profilu jeden", + "color2": "Kolor profilu dwa", + "header": "Wpisz nazwę swojego urządzenia i wybierz kolory oraz ikonę użytkownika", + "icon": "Ikona użytkownika", + "next": "Dalej", + "title": "Informacje o koncie" + } + }, + "trust": { + "failed": { + "text": "Skonfigurowałeś to poprawnie?", + "title": "Nie udało się połączyć z serwerem" + }, + "host": "Łączysz się z <0>{{hostname}} - przed utworzeniem konta potwierdź że ufasz temu serwerowi", + "no": "Wstecz", + "title": "Ufasz temu serwerowi?", + "yes": "Ufam temu serwerowi" + }, + "verify": { + "description": "Wprowadź swoją frazę hasła, aby potwierdzić jego zapisanie i utworzyć konto", + "invalidData": "Dane są nieprawidłowe", + "noMatch": "Fraza hasła nie pasuje", + "passphraseLabel": "Twoja 12-wyrazowa fraza hasła", + "recaptchaFailed": "Weryfikacja ReCaptcha nie powiodła się", + "register": "Utwórz konto", + "title": "Potwierdź swoją frazę hasła" + } + }, + "errors": { + "badge": "Zepsuło się", + "details": "Szczegóły błędu", + "reloadPage": "Odśwież stronę", + "showError": "Pokaż szczegóły błędu", + "title": "Napotkaliśmy błąd!" + }, + "footer": { + "legal": { + "disclaimer": "Zastrzeżenie", + "disclaimerText": "sudo-flix nie przechowuje żadnych plików, zawiera jedynie łącza do usług stron trzecich. Kwestie prawne należy konsultować z hostami plików i dostawcami. sudo-flix nie ponosi odpowiedzialności za jakiekolwiek pliki multimedialne prezentowane przez dostawców wideo." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Oglądaj swoje ulubione seriale i filmy za pomocą tej aplikacji streamingowej typu open source." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Informacje", + "dmca": "DMCA", + "login": "Zaloguj sie", + "onboarding": "Konfiguracja", + "pagetitle": "{{title}} - sudo-flix", + "register": "Zarejestruj się", + "settings": "Ustawienia" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Zakładki" + }, + "continueWatching": { + "sectionTitle": "Kontynuuj oglądanie" + }, + "mediaList": { + "stopEditing": "Przestań edytować" + }, + "search": { + "allResults": "To wszystko co mamy!", + "failed": "Nie udało się znaleźć mediów, Spróbuj ponownie!", + "loading": "Wczytywanie...", + "noResults": "Nie mogliśmy niczego znaleźć!", + "placeholder": "Co chciałbyś obejrzeć?", + "sectionTitle": "Wyniki wyszukiwania" + }, + "titles": { + "day": { + "default": "Co chciałbyś obejrzeć dziś po południu?", + "extra": [ + "Masz ochotę na przygodę? Park Jurajski może być idealnym wyborem." + ] + }, + "morning": { + "default": "Co chciałbyś obejrzeć dziś rano?", + "extra": ["Słyszałem że „Przed wschodem słońca” jest dobre"] + }, + "night": { + "default": "Co chciałbyś obejrzeć dziś wieczorem?", + "extra": ["Zmęczony? Słyszałem że „Egzorcysta” jest dobry."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Serial" + } + }, + "navigation": { + "banner": { + "offline": "Sprawdź swoje połączenie sieciowe" + }, + "menu": { + "about": "O nas", + "donation": "Wspieraj nas", + "logout": "Wyloguj się", + "register": "Synchronizuj z chmurą", + "settings": "Ustawienia", + "support": "Wsparcie" + } + }, + "notFound": { + "badge": "Nie znaleziono", + "goHome": "Wróć na stronę główną", + "message": "Szukaliśmy wszędzie: w koszu, w szafie, a nawet w piwnicy, ale nie byliśmy w stanie znaleźć strony której szukasz.", + "title": "Nie można znaleźć tej strony" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Anuluj", + "confirm": "Użyj domyślnej konfiguracji", + "description": "Domyślna konfiguracja nie ma najlepszych strumieni i może być strasznie powolna.", + "title": "Jesteś pewien?" + }, + "extension": { + "back": "Wstecz", + "explainer": "Korzystając z rozszerzenia przeglądarki, możesz uzyskać najlepsze strumienie. Wystarczy prosta instalacja.", + "explainerIos": "Niestety, rozszerzenie przeglądarki nie jest obsługiwane w systemie iOS, naciśnij Wstecz, aby wybrać inną opcję.", + "extensionHelp": "Jeżeli zainstalowałeś rozszerzenie, ale nie zostało ono wykryte. Otwórz rozszerzenie za pomocą menu rozszerzeń przeglądarki i postępuj zgodnie z instrukcjami wyświetlanymi na ekranie.", + "linkChrome": "Zainstaluj rozszerzenie na Chrome", + "linkFirefox": "Zainstaluj rozszerzenie na Firefox", + "notDetecting": "Zainstalowano na Chrome, ale się nie wyświetla? Spróbuj odświeżyć stronę!", + "notDetectingAction": "Odśwież stronę", + "status": { + "disallowed": "Rozszerzenie nie jest włączone dla tej strony", + "disallowedAction": "Włącz rozszerzenie", + "failed": "Nie udało się uzyskać statusu", + "loading": "Oczekiwanie na instalację rozszerzenia", + "outdated": "Za stara wersja rozszerzenia", + "success": "Rozszerzenie działa tak jak powinno!" + }, + "submit": "Kontynuuj", + "title": "Zacznijmy od rozszerzenia" + }, + "proxy": { + "back": "Wstecz", + "explainer": "Za pomocą metody proxy można uzyskać strumienie o doskonałej jakości, tworząc samoobsługowy serwer proxy.", + "input": { + "errorConnection": "Nie można połączyć się z serwerem proxy", + "errorInvalidUrl": "Nieprawidłowy adres URL", + "errorNotProxy": "Spodziewano proxy, ale otrzymano stronę internetową", + "label": "Adres URL serwera proxy", + "placeholder": "https://" + }, + "link": "Dowiedz się, jak utworzyć proxy", + "submit": "Prześlij proxy", + "title": "Stwórzmy nowe proxy" + }, + "start": { + "explainer": "Aby uzyskać najlepsze transmisje strumieniowe. Będziesz musiał wybrać metodę strumieniowania, której chcesz użyć.", + "options": { + "default": { + "text": "Nie chcę dobrej jakości strumieni, <0 /> <1>użyj domyślnej konfiguracji" + }, + "extension": { + "action": "Zainstaluj rozszerzenie", + "description": "Zainstaluj rozszerzenie przeglądarki i uzyskaj dostęp do najlepszych źródeł.", + "quality": "Najlepsza jakość", + "title": "Rozszerzenie przeglądarki" + }, + "proxy": { + "action": "Skonfiguruj serwer proxy", + "description": "Skonfiguruj proxy w 5 minut i uzyskaj dostęp do świetnych źródeł.", + "quality": "Dobra jakość", + "title": "Niestandardowy serwer proxy" + } + }, + "title": "Przejdźmy do konfiguracji z sudo-flix" + } + }, + "overlays": { + "close": "Zamknij" + }, + "player": { + "back": { + "default": "Wróć na stronę główną", + "short": "Wróć" + }, + "casting": { + "enabled": "Przesyłam na urządzenie..." + }, + "menus": { + "downloads": { + "disclaimer": "Pliki do pobrania są pobierane bezpośrednio od dostawcy. sudo-flix nie ma kontroli nad sposobem dostarczania plików do pobrania.", + "downloadPlaylist": "Pobierz playlistę", + "downloadSubtitle": "Pobierz aktualne napisy", + "downloadVideo": "Pobierz wideo", + "hlsDisclaimer": "Pliki do pobrania są pobierane bezpośrednio od dostawcy. sudo-flix nie ma kontroli nad sposobem dostarczania plików do pobrania.

Należy pamiętać, że pobierasz playlistę HLS, nie zaleca się pobierania, jeśli nie jesteś zaznajomiony z zaawansowanymi formatami przesyłania strumieniowego. Wypróbuj różne źródła dla innych formatów.", + "onAndroid": { + "1": "Aby pobrać na Androidzie, kliknij przycisk pobierania, a następnie na nowej stronie dotknij i przytrzymaj na filmie, a następnie wybierz zapisz.", + "shortTitle": "Pobierz / Android", + "title": "Pobieranie na Androidzie" + }, + "onIos": { + "1": "Aby pobrać na iOS, kliknij przycisk pobierania, a następnie na nowej stronie, kliknij , a następnie Zapisz do plików .", + "shortTitle": "Pobierz / iOS", + "title": "Pobieranie na iOS" + }, + "onPc": { + "1": "Na komputerze, kliknij przycisk pobierania, a następnie na nowej stronie, kliknij wideo prawym przyciskiem myszy i wybierz Zapisz wideo jako", + "shortTitle": "Pobierz / Komputer", + "title": "Pobieranie na komputerze" + }, + "title": "Pobierz" + }, + "episodes": { + "button": "Odcinki", + "emptyState": "Nie ma odcinków w tym sezonie, sprawdź później!", + "episodeBadge": "E{{episode}}", + "loadingError": "Błąd podczas ładowania sezonu", + "loadingList": "Wczytywanie...", + "loadingTitle": "Wczytywanie...", + "unairedEpisodes": "Jeden lub więcej odcinków tego sezonu zostało wyłączonych, ponieważ nie zostały jeszcze wyemitowane." + }, + "playback": { + "speedLabel": "Szybkość odtwarzania", + "title": "Ustawienia odtwarzania" + }, + "quality": { + "automaticLabel": "Jakość automatyczna", + "hint": "Możesz spróbować <0>przełączyć źródło aby uzyskać różne opcje jakości.", + "iosNoQuality": "Ze względu na ograniczenia zdefiniowane przez Apple, wybór jakości nie jest dostępny w systemie iOS dla tego źródła. Możesz spróbować <0>przełączyć na inne źródło aby uzyskać inne opcje jakości.", + "title": "Jakość" + }, + "settings": { + "downloadItem": "Pobierz", + "enableSubtitles": "Włącz napisy", + "experienceSection": "Odtwarzanie", + "playbackItem": "Ustawienia odtwarzania", + "qualityItem": "Jakość", + "sourceItem": "Źródła wideo", + "subtitleItem": "Ustawienia napisów", + "videoSection": "Ustawienia wideo" + }, + "sources": { + "failed": { + "text": "Wystąpił błąd podczas próby znalezienia filmów. Spróbuj użyć innego źródła.", + "title": "Nie udało się zgarnąć" + }, + "noEmbeds": { + "text": "Nie znaleźliśmy żadnych osadzonych plików. Spróbuj użyć innego źródła.", + "title": "Nie znaleziono osadzonych treści" + }, + "noStream": { + "text": "To źródło nie ma strumieni dla tego filmu lub serialu.", + "title": "Brak strumienia" + }, + "title": "Źródła", + "unknownOption": "Nieznany" + }, + "subtitles": { + "customChoice": "Wybierz napisy z pliku", + "customizeLabel": "Dostosuj", + "offChoice": "Wyłączone", + "settings": { + "backlink": "Własne napisy", + "delay": "Opóźnienie napisów", + "fixCapitals": "Popraw kapitalizację" + }, + "title": "Napisy", + "unknownLanguage": "Nieznany" + } + }, + "metadata": { + "api": { + "text": "Nie można załadować metadanych API, sprawdź połączenie internetowe.", + "title": "Nie udało się załadować metadanych API" + }, + "dmca": { + "badge": "Usunięte", + "text": "Ten materiał nie jest już dostępny z powodu żądania usunięcia lub roszczenia dotyczącego praw autorskich.", + "title": "Media zostały usunięte" + }, + "extensionPermission": { + "badge": "Brak uprawnień", + "button": "Użyj rozszerzenia", + "text": "Masz nasze rozszerzenie przeglądarki, ale potrzebujemy pozwolenia, aby zacząć z niego korzystać.", + "title": "Skonfiguruj rozszerzenie" + }, + "failed": { + "badge": "Nie powiodło się", + "homeButton": "Wróć na stronę główną", + "text": "Nie można załadować metadanych multimediów z TMDB. Sprawdź czy TMDB nie działa lub jest zablokowany na twoim połączeniu internetowym.", + "title": "Nie udało się załadować metadanych" + }, + "notFound": { + "badge": "Nie znaleziono", + "homeButton": "Wróć na stronę główną", + "text": "Nie znaleźliśmy żądanych multimediów. Albo został usunięty, albo zmieniłeś adres URL.", + "title": "Nie można znaleźć multimediów." + } + }, + "nextEpisode": { + "cancel": "Anuluj", + "next": "Następny odcinek" + }, + "playbackError": { + "badge": "Błąd odtwarzania", + "errors": { + "errorAborted": "Pobieranie multimediów zostało przerwane na żądanie użytkownika.", + "errorDecode": "Pomimo wcześniejszego stwierdzenia że zasób multimedialny nadaje się do użytku, wystąpił błąd podczas próby dekodowania zasobu multimedialnego, co spowodowało błąd.", + "errorGenericMedia": "Wystąpił nieznany błąd multimediów.", + "errorNetwork": "Wystąpił jakiś błąd sieciowy, który uniemożliwił pomyślne pobranie multimediów, mimo że były one wcześniej dostępne.", + "errorNotSupported": "Obiekt multimediów lub dostawcy multimediów nie jest obsługiwany." + }, + "homeButton": "Wróć na stronę główną", + "text": "Wystąpił błąd podczas próby odtworzenia multimediów. Spróbuj ponownie.", + "title": "Nie udało się odtworzyć wideo!" + }, + "scraping": { + "items": { + "failure": "Wystąpił błąd", + "notFound": "Nie ma wideo", + "pending": "Sprawdzanie filmów..." + }, + "notFound": { + "badge": "Nie znaleziono", + "detailsButton": "Pokaż szczegóły", + "homeButton": "Wróć na stronę główną", + "text": "Przeszukaliśmy naszych dostawców i nie możemy znaleźć poszukiwanych multimediów! Nie przechowujemy multimediów i nie mamy kontroli nad ich dostępnością. Kliknij \"Pokaż szczegóły\" poniżej, aby uzyskać więcej informacji.", + "title": "Nie mogliśmy znaleźć tego" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "Zostało {{timeLeft}} • Skończ o {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Proszę potwierdź że jesteś człowiekiem, wypełniając Captcha po prawej stronie. Ma to zapewnić bezpieczeństwo sudo-flix!", + "error": "Nie udało się zweryfikować Twojego człowieczeństwa. Proszę spróbuj ponownie.", + "title": "Musimy sprawdzić, czy jesteś człowiekiem.", + "verifyingHumanity": "Sprawdzasz swoje człowieczeństwo..." + } + }, + "screens": { + "dmca": { + "text": "Witamy na stronie kontaktowej DMCA serwisu sudo-flix! Szanujemy prawa własności intelektualnej i chcemy szybko rozwiązywać wszelkie problemy związane z prawami autorskimi. Jeśli uważasz że twoja praca chroniona prawem autorskim została niewłaściwie wykorzystana na naszej platformie, proszę wysłać szczegółowe powiadomienie DMCA na poniższy adres e-mail. Prosimy o dołączenie opisu materiału chronionego prawem autorskim, danych kontaktowych i oświadczenia w dobrej wierze. Zależy nam na szybkim rozwiązywaniu takich spraw i będziemy wdzięczni za współpracę w utrzymaniu sudo-flix jako miejsce w którym szanuje się kreatywność i prawa autorskie.", + "title": "DMCA" + }, + "loadingApp": "Wczytywanie aplikacji", + "loadingUser": "Wczytywanie profilu", + "loadingUserError": { + "logout": "Wyloguj się", + "reset": "Zresetuj serwer niestandardowy", + "text": "Nie udało się wczytać profilu", + "textWithReset": "Nie udało się wczytać profilu z niestandardowego serwera, chcesz powrócić do domyślnego serwera?" + }, + "migration": { + "failed": "Migracja danych nie powiodła się.", + "inProgress": "Proszę czekać, przeprowadzamy migrację danych. Nie powinno to zająć dużo czasu." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nazwa urządzenia", + "deviceNamePlaceholder": "Telefon osobisty", + "editProfile": "Edytuj", + "logoutButton": "Wyloguj się" + }, + "actions": { + "delete": { + "button": "Usuń konto", + "confirmButton": "Usuń konto", + "confirmDescription": "Na pewno chcesz usunąć swoje konto? Wszystkie twoje dane zostaną stracone!", + "confirmTitle": "Jesteś pewny?", + "text": "Działanie to jest nieodwracalne. Wszystkie dane zostaną usunięte i nie będzie można ich odzyskać.", + "title": "Usuń konto" + }, + "title": "Działania" + }, + "devices": { + "deviceNameLabel": "Nazwa urządzenia", + "failed": "Nie udało się załadować sesji", + "removeDevice": "Usuń", + "title": "Urządzenia" + }, + "profile": { + "finish": "Zakończ edycję", + "firstColor": "Kolor profilu jeden", + "secondColor": "Kolor profilu dwa", + "title": "Edytuj zdjęcie profilowe", + "userIcon": "Ikona użytkownika" + }, + "register": { + "cta": "Rozpocznij", + "text": "Udostępnij postępy oglądania między urządzeniami i synchronizuj je.", + "title": "Synchronizuj z chmurą" + }, + "title": "Konto" + }, + "appearance": { + "activeTheme": "Aktywny", + "themes": { + "blue": "Niebieski", + "default": "Domyślny", + "gray": "Szary", + "red": "Czerwony", + "teal": "Morski" + }, + "title": "Wygląd" + }, + "connections": { + "server": { + "description": "Jeśli chcesz połączyć się z niestandardowym backendem w celu przechowywania danych, włącz tę opcję i podaj adres URL. <0>Instrukcje.", + "label": "Serwer niestandardowy", + "urlLabel": "Adres URL niestandardowego serwera" + }, + "setup": { + "doSetup": "Konfiguruj", + "errorStatus": { + "description": "Wygląda na to, że jeden lub więcej elementów tej konfiguracji wymaga Twojej uwagi.", + "title": "Coś wymaga twojej uwagi" + }, + "itemError": "Coś jest nie tak z tym ustawieniem. Ponownie przejdź przez konfigurację, aby to naprawić.", + "items": { + "default": "Domyślna konfiguracja", + "extension": "Rozszerzenie", + "proxy": "Niestandardowy serwer proxy" + }, + "redoSetup": "Ponów konfigurację", + "successStatus": { + "description": "Wszystko jest gotowe, abyś mógł zacząć oglądać ulubione media.", + "title": "Wszystko jest gotowe!" + }, + "unsetStatus": { + "description": "Kliknij przycisk po prawej stronie, aby rozpocząć proces konfiguracji.", + "title": "Nie przeszedłeś konfiguracji" + } + }, + "title": "Połączenia", + "workers": { + "addButton": "Dodaj nowego pracownika", + "description": "Aby aplikacja działała, cały ruch jest kierowany przez serwery proxy. Włącz tę opcję, jeśli chcesz korzystać z własnych pracowników. <0>Instrukcje.", + "emptyState": "Brak pracowników, dodaj jednego poniżej", + "label": "Używaj niestandardowych pracowników proxy", + "urlLabel": "Adresy URL pracowników", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Język aplikacji", + "languageDescription": "Język zastosowany do całej aplikacji.", + "thumbnail": "Generuj miniatury", + "thumbnailDescription": "W większości przypadków filmy nie mają miniatur. Możesz włączyć to ustawienie, aby generować je na bieżąco, ale może to spowolnić wideo.", + "thumbnailLabel": "Generuj miniatury", + "title": "Preferencje" + }, + "reset": "Reset", + "save": "Zapisz", + "sidebar": { + "info": { + "appVersion": "Wersja aplikacji", + "backendUrl": "Adres URL backendu", + "backendVersion": "Wersja backendu", + "hostname": "Nazwa hosta", + "insecure": "Niebezpieczny", + "notLoggedIn": "Nie jesteś zalogowany", + "secure": "Bezpieczne", + "title": "Informacje o aplikacji", + "unknownVersion": "Nieznany", + "userId": "Identyfikator użytkownika" + } + }, + "subtitles": { + "backgroundLabel": "Krycie tła", + "colorLabel": "Kolor", + "previewQuote": "Nie wolno mi się bać. Strach zabija myślenie.", + "textSizeLabel": "Rozmiar czcionki", + "title": "Napisy" + }, + "unsaved": "Masz niezapisane zmiany" + } +} diff --git a/src/assets/locales/pt-BR.json b/src/assets/locales/pt-BR.json new file mode 100644 index 00000000..adc20539 --- /dev/null +++ b/src/assets/locales/pt-BR.json @@ -0,0 +1,491 @@ +{ + "about": { + "description": "sudo-flix é uma aplicação web que busca na internet por streams. A equipe visa uma abordagem principalmente minimalista para consumir conteúdo.", + "faqTitle": "Perguntas comuns", + "q1": { + "body": "sudo-flix não hospeda nenhum conteúdo. Quando você clica em algo para assistir, a internet é pesquisada pela mídia selecionada (Na tela de carregamento e na aba 'fontes de vídeo' você pode ver qual fonte está usando). A mídia nunca é carregada pelo sudo-flix, tudo é através deste mecanismo de busca.", + "title": "De onde vem o conteúdo?" + }, + "q2": { + "body": "Não é possível solicitar um filme ou série, o sudo-flix não gerencia nenhum conteúdo. Todo o conteúdo é visualizado através de fontes na internet.", + "title": "Onde posso solicitar um filme ou série?" + }, + "q3": { + "body": "Nossos resultados de pesquisa são alimentados pelo The Movie Database (TMDB) e são exibidos independentemente de nossas fontes terem ou não o conteúdo.", + "title": "Os resultados da pesquisa mostram o filme ou série, por que não consigo reproduzi-lo?" + }, + "title": "Sobre o sudo-flix" + }, + "actions": { + "copied": "Copiado", + "copy": "Copiar" + }, + "auth": { + "createAccount": "Ainda não tem uma conta? <0>Crie uma conta.", + "deviceNameLabel": "Nome do dispositivo", + "deviceNamePlaceholder": "Telefone pessoal", + "generate": { + "description": "Sua frase de acesso funciona como seu nome de usuário e senha. Certifique-se de mantê-la segura, pois você precisará inseri-la para entrar na sua conta", + "next": "Eu salvei minha frase de acesso", + "passphraseFrameLabel": "Frase de acesso", + "title": "Sua frase de acesso" + }, + "hasAccount": "Já tem uma conta? <0>Entre aqui.", + "login": { + "description": "Por favor, insira sua frase de acesso para entrar na sua conta", + "deviceLengthError": "Por favor, insira um nome para o dispositivo", + "passphraseLabel": "Frase de acesso de 12 palavras", + "passphrasePlaceholder": "Frase de acesso", + "submit": "Entrar", + "title": "Entrar na sua conta", + "validationError": "Frase de acesso incorreta ou incompleta" + }, + "register": { + "information": { + "color1": "Cor do perfil um", + "color2": "Cor do perfil dois", + "header": "Digite um nome para o seu dispositivo e escolha cores e um ícone de usuário de sua preferência", + "icon": "Ícone do usuário", + "next": "Próximo", + "title": "Informações da conta" + } + }, + "trust": { + "failed": { + "text": "Você o configurou corretamente?", + "title": "Falha ao alcançar o servidor" + }, + "host": "Você está se conectando a <0>{{hostname}} - por favor, confirme se confia nele antes de criar uma conta", + "no": "Voltar", + "title": "Você confia neste servidor?", + "yes": "Eu confio neste servidor" + }, + "verify": { + "description": "Por favor, insira sua frase de acesso anterior para confirmar que você a salvou e para criar sua conta", + "invalidData": "Dados não são válidos", + "noMatch": "Frase de acesso não corresponde", + "passphraseLabel": "Sua frase de acesso de 12 palavras", + "recaptchaFailed": "Falha na validação do ReCaptcha", + "register": "Criar conta", + "title": "Confirme sua frase de acesso" + } + }, + "errors": { + "badge": "Quebrou", + "details": "Detalhes do erro", + "reloadPage": "Recarregar a página", + "showError": "Mostrar detalhes do erro", + "title": "Encontramos um erro!" + }, + "footer": { + "legal": { + "disclaimer": "Aviso legal", + "disclaimerText": "sudo-flix não hospeda nenhum arquivo, apenas fornece links para serviços de terceiros. Questões legais devem ser tratadas com os hosts de arquivos e provedores. sudo-flix não é responsável por quaisquer arquivos de mídia exibidos pelos provedores de vídeo." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Assista seus programas e filmes favoritos com este aplicativo de streaming de código aberto." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Sobre", + "dmca": "DMCA", + "login": "Entrar", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registrar", + "settings": "Configurações" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Favoritos" + }, + "continueWatching": { + "sectionTitle": "Continue assistindo" + }, + "mediaList": { + "stopEditing": "Parar de editar" + }, + "search": { + "allResults": "Isso é tudo que temos!", + "failed": "Falha ao encontrar mídia, tente novamente!", + "loading": "Carregando...", + "noResults": "Não conseguimos encontrar nada!", + "placeholder": "O que você quer assistir?", + "sectionTitle": "Resultados da pesquisa" + }, + "titles": { + "day": { + "default": "O que você gostaria de assistir esta tarde?", + "extra": [ + "Se sentindo aventureiro? Jurassic Park pode ser a escolha perfeita." + ] + }, + "morning": { + "default": "O que você gostaria de assistir esta manhã?", + "extra": ["Ouvi dizer que Antes do Amanhecer é bom"] + }, + "night": { + "default": "O que você gostaria de assistir esta noite?", + "extra": ["Cansado? Ouvi dizer que O Exorcista é bom."] + } + } + }, + "media": { + "episodeDisplay": "T{{season}} E{{episode}}", + "types": { + "movie": "Filme", + "show": "Série" + } + }, + "navigation": { + "banner": { + "offline": "Verifique sua conexão com a internet" + }, + "menu": { + "about": "Sobre nós", + "donation": "Doar", + "logout": "Sair", + "register": "Sincronizar com a nuvem", + "settings": "Configurações", + "support": "Suporte" + } + }, + "notFound": { + "badge": "Não encontrado", + "goHome": "Voltar para o início", + "message": "Procuramos em todos os lugares: debaixo das lixeiras, no armário, atrás do proxy, mas no final não conseguimos encontrar a página que você está procurando.", + "title": "Não conseguimos encontrar essa página" + }, + "onboarding": { + "start": { + "explainer": "Para obter os melhores streams possíveis. Você precisará escolher qual método de streaming deseja usar.", + "options": { + "default": { + "text": "Eu não quero streams de boa qualidade, <0 /> <1>use a configuração padrão" + }, + "extension": { + "action": "Instalar extensão", + "description": "Instale a extensão de navegador e tenha acesso às melhores fontes.", + "quality": "Melhor qualidade", + "title": "Extensão de navegador" + }, + "proxy": { + "action": "Configurar proxy", + "description": "Configure um proxy em apenas 5 minutos e tenha acesso a ótimas fontes.", + "quality": "Boa qualidade", + "title": "Proxy personalizado" + } + }, + "title": "Vamos juntos configurar o sudo-flix" + } + }, + "overlays": { + "close": "Fechar" + }, + "player": { + "back": { + "default": "Voltar para o início", + "short": "Voltar" + }, + "casting": { + "enabled": "Transmitindo para o dispositivo..." + }, + "menus": { + "downloads": { + "disclaimer": "Os downloads são feitos diretamente do provedor. sudo-flix não tem controle sobre como os downloads são fornecidos.", + "downloadPlaylist": "Baixar playlist", + "downloadSubtitle": "Baixar legenda atual", + "downloadVideo": "Baixar vídeo", + "hlsDisclaimer": "Os downloads são feitos diretamente do provedor. sudo-flix não tem controle sobre como os downloads são fornecidos.

Por favor, note que você está baixando uma playlist HLS, isso é destinado para usuários familiarizados com streaming multimídia avançado.. Experimente outras fontes para formatos diferentes.", + "onAndroid": { + "1": "Para baixar no Android, clique no botão de download e, na nova página, toque e segure no vídeo, depois selecione salvar.", + "shortTitle": "Baixar / Android", + "title": "Baixando no Android" + }, + "onIos": { + "1": "Para baixar no iOS, clique no botão de download e, na nova página, clique em , depois em Salvar em Arquivos .", + "shortTitle": "Baixar / iOS", + "title": "Baixando no iOS" + }, + "onPc": { + "1": "No PC, clique no botão de download e, na nova página, clique com o botão direito do mouse no vídeo e selecione Salvar vídeo como", + "shortTitle": "Baixar / PC", + "title": "Baixando no PC" + }, + "title": "Baixar" + }, + "episodes": { + "button": "Episódios", + "emptyState": "Não há episódios nesta temporada, volte mais tarde!", + "episodeBadge": "E{{episode}}", + "loadingError": "Erro ao carregar temporada", + "loadingList": "Carregando...", + "loadingTitle": "Carregando...", + "unairedEpisodes": "Um ou mais episódios nesta temporada foram desabilitados porque ainda não foram ao ar." + }, + "playback": { + "speedLabel": "Velocidade de reprodução", + "title": "Configurações de reprodução" + }, + "quality": { + "automaticLabel": "Qualidade automática", + "hint": "Você pode tentar <0>trocar de fonte para obter diferentes opções de qualidade.", + "iosNoQuality": "Devido a limitações definidas pela Apple, a seleção de qualidade não está disponível no iOS para esta fonte. Você pode tentar <0>trocar para outra fonte para obter diferentes opções de qualidade.", + "title": "Qualidade" + }, + "settings": { + "downloadItem": "Baixar", + "enableSubtitles": "Ativar Legendas", + "experienceSection": "Experiência de visualização", + "playbackItem": "Configurações de reprodução", + "qualityItem": "Qualidade", + "sourceItem": "Fontes de vídeo", + "subtitleItem": "Configurações de legenda", + "videoSection": "Configurações de vídeo" + }, + "sources": { + "failed": { + "text": "Houve um erro ao tentar encontrar vídeos, por favor, tente uma fonte diferente.", + "title": "Falha ao raspar" + }, + "noEmbeds": { + "text": "Não conseguimos encontrar nenhum incorporado, por favor, tente uma fonte diferente.", + "title": "Nenhum incorporado encontrado" + }, + "noStream": { + "text": "Esta fonte não tem transmissões para este filme ou série.", + "title": "Sem transmissão" + }, + "title": "Fontes", + "unknownOption": "Desconhecido" + }, + "subtitles": { + "customChoice": "Selecione arquivo de legenda", + "customizeLabel": "Personalizar", + "offChoice": "Desativadas", + "settings": { + "backlink": "Legendas personalizadas", + "delay": "Atraso da legenda", + "fixCapitals": "Corrigir letras maiúsculas" + }, + "title": "Legendas", + "unknownLanguage": "Desconhecido" + } + }, + "metadata": { + "api": { + "text": "Não foi possível carregar os metadados da API. Por favor, verifique sua conexão com a internet.", + "title": "Falha ao carregar os metadados da API" + }, + "dmca": { + "badge": "Removido", + "text": "Esta mídia não está mais disponível devido a um aviso de remoção ou reivindicação de direitos autorais.", + "title": "A mídia foi removida" + }, + "extensionPermission": { + "button": "Usar extensão", + "text": "Você possui a extensão de navegador, mas nós precisamos de sua permissão para começar a usá-la.", + "title": "Configure a extensão" + }, + "failed": { + "badge": "Falhou", + "homeButton": "Ir para o início", + "text": "Não foi possível carregar os metadados da mídia do TMDB. Por favor, verifique se o TMDB está fora do ar ou bloqueado na sua conexão de internet.", + "title": "Falha ao carregar metadados" + }, + "notFound": { + "badge": "Não encontrado", + "homeButton": "Voltar para o início", + "text": "Não conseguimos encontrar a mídia que você solicitou. Ou ela foi removida ou você alterou o URL.", + "title": "Não conseguimos encontrar essa mídia." + } + }, + "nextEpisode": { + "cancel": "Cancelar", + "next": "Próximo episódio" + }, + "playbackError": { + "badge": "Erro de reprodução", + "errors": { + "errorAborted": "A busca pela mídia foi abortada a pedido do usuário.", + "errorDecode": "Apesar de ter sido previamente determinado como utilizável, ocorreu um erro ao tentar decodificar o recurso de mídia, resultando em um erro.", + "errorGenericMedia": "Ocorreu um erro desconhecido de mídia.", + "errorNetwork": "Ocorreu algum tipo de erro de rede que impediu a mídia de ser buscada com sucesso, apesar de ter sido previamente disponível.", + "errorNotSupported": "A mídia ou objeto do provedor de mídia não é suportado." + }, + "homeButton": "Ir para o início", + "text": "Houve um erro ao tentar reproduzir a mídia. Por favor, tente novamente.", + "title": "Falha ao reproduzir o vídeo!" + }, + "scraping": { + "items": { + "failure": "Ocorreu um erro", + "notFound": "Não tem o vídeo", + "pending": "Verificando vídeos..." + }, + "notFound": { + "badge": "Não encontrado", + "detailsButton": "Mostrar detalhes", + "homeButton": "Ir para o início", + "text": "Procuramos em nossos provedores e não conseguimos encontrar a mídia que você está procurando! Não hospedamos a mídia e não temos controle sobre o que está disponível. Por favor, clique em 'Mostrar detalhes' abaixo para mais informações.", + "title": "Não conseguimos encontrar isso" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} restantes • Terminar às {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Por favor, confirme que você é um humano completando o Captcha à direita. Isso é para manter sudo-flix seguro!", + "error": "Falha ao verificar sua humanidade. Por favor, tente novamente.", + "title": "Precisamos verificar se você é humano.", + "verifyingHumanity": "Verificando sua humanidade..." + } + }, + "screens": { + "dmca": { + "text": "Bem-vindo à página de contato do DMCA do sudo-flix! Respeitamos os direitos de propriedade intelectual e queremos resolver quaisquer preocupações com direitos autorais rapidamente. Se você acredita que seu trabalho protegido por direitos autorais foi usado indevidamente em nossa plataforma, envie um aviso detalhado de DMCA para o e-mail abaixo. Inclua uma descrição do material protegido por direitos autorais, seus detalhes de contato e uma declaração de crença de boa fé. Estamos comprometidos em resolver essas questões prontamente e agradecemos sua cooperação em manter o sudo-flix um lugar que respeita a criatividade e os direitos autorais.", + "title": "DMCA" + }, + "loadingApp": "Carregando aplicativo", + "loadingUser": "Carregando seu perfil", + "loadingUserError": { + "logout": "Sair", + "reset": "Redefinir servidor personalizado", + "text": "Falha ao carregar seu perfil", + "textWithReset": "Falha ao carregar seu perfil do seu servidor personalizado, deseja redefinir para o servidor padrão?" + }, + "migration": { + "failed": "Falha ao migrar seus dados.", + "inProgress": "Por favor, aguarde, estamos migrando seus dados. Isso não deve demorar muito." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nome do dispositivo", + "deviceNamePlaceholder": "Telefone pessoal", + "editProfile": "Editar", + "logoutButton": "Sair" + }, + "actions": { + "delete": { + "button": "Excluir conta", + "confirmButton": "Excluir conta", + "confirmDescription": "Você tem certeza de que deseja excluir sua conta? Todos os seus dados serão perdidos!", + "confirmTitle": "Você tem certeza?", + "text": "Esta ação é irreversível. Todos os dados serão excluídos e nada poderá ser recuperado.", + "title": "Excluir conta" + }, + "title": "Ações" + }, + "devices": { + "deviceNameLabel": "Nome do dispositivo", + "failed": "Falha ao carregar sessões", + "removeDevice": "Remover", + "title": "Dispositivos" + }, + "profile": { + "finish": "Finalizar edição", + "firstColor": "Cor do perfil um", + "secondColor": "Cor do perfil dois", + "title": "Editar foto de perfil", + "userIcon": "Ícone do usuário" + }, + "register": { + "cta": "Começar", + "text": "Compartilhe seu progresso de visualização entre dispositivos e mantenha-os sincronizados.", + "title": "Sincronizar com a nuvem" + }, + "title": "Conta" + }, + "appearance": { + "activeTheme": "Ativo", + "themes": { + "blue": "Azul", + "default": "Padrão", + "gray": "Cinza", + "red": "Vermelho", + "teal": "Verde-azulado" + }, + "title": "Aparência" + }, + "connections": { + "server": { + "description": "Se você deseja se conectar a um backend personalizado para armazenar seus dados, ative isso e forneça a URL. <0>Instruções.", + "label": "Servidor personalizado", + "urlLabel": "URL do servidor personalizado" + }, + "setup": { + "doSetup": "Configurar", + "errorStatus": { + "description": "Parece que um ou mais itens nesta configuração necessitam sua atenção.", + "title": "Algo necessita de sua atenção" + }, + "itemError": "Há algo errado com esta configuração. Realize uma nova configuração para corrigir.", + "items": { + "default": "Configuração padrão", + "extension": "Extensão", + "proxy": "Proxy personalizado" + }, + "redoSetup": "Refazer configuração", + "successStatus": { + "description": "Tudo está pronto para você começar a assistir sua mídia favorita.", + "title": "Está tudo pronto!" + }, + "unsetStatus": { + "description": "Por favor, clique no botão à direita para iniciar a configuração.", + "title": "Você não realizou a configuração" + } + }, + "title": "Conexões", + "workers": { + "addButton": "Adicionar novo worker", + "description": "Para fazer o aplicativo funcionar, todo o tráfego é roteado através de proxies. Ative isso se você quiser trazer seus próprios workers. <0>Instruções.", + "emptyState": "Ainda não há workers, adicione um abaixo", + "label": "Usar proxy workers personalizados", + "urlLabel": "URLs dos workers", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Idioma do aplicativo", + "languageDescription": "Idioma aplicado para todo o aplicativo.", + "thumbnail": "Gerar miniaturas", + "thumbnailDescription": "Na maior parte do tempo, os vídeos não possuem miniaturas. Você pode ativar esta configuração para gerá-las dinamicamente, mas elas podem tornar seu vídeo mais lento.", + "thumbnailLabel": "Gerar miniaturas", + "title": "Preferências" + }, + "reset": "Redefinir", + "save": "Salvar", + "sidebar": { + "info": { + "appVersion": "Versão do aplicativo", + "backendUrl": "URL do backend", + "backendVersion": "Versão do backend", + "hostname": "Nome do host", + "insecure": "Inseguro", + "notLoggedIn": "Você não está logado", + "secure": "Seguro", + "title": "Informações do aplicativo", + "unknownVersion": "Desconhecido", + "userId": "ID do usuário" + } + }, + "subtitles": { + "backgroundLabel": "Opacidade do fundo", + "colorLabel": "Cor", + "previewQuote": "Eu não devo ter medo. Medo é o assassino da mente.", + "textSizeLabel": "Tamanho do texto", + "title": "Legendas" + }, + "unsaved": "Você tem alterações não salvas" + } +} diff --git a/src/assets/locales/pt-PT.json b/src/assets/locales/pt-PT.json new file mode 100644 index 00000000..4a121433 --- /dev/null +++ b/src/assets/locales/pt-PT.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "sudo-flix é uma aplicação web que pesquisa a internet por streams. A equipa visa uma abordagem maioritariamente minimalista na consumação de conteúdos.", + "faqTitle": "Perguntas frequentes", + "q1": { + "body": "sudo-flix não hospeda nenhum conteúdo. Quando clica para assistir a algo, a internet é pesquisada para o media selecionado (Na tela de carregamento e na aba 'fontes de vídeo', pode ver qual a fonte que está a ser utilizada). O media nunca é carregado pelo sudo-flix, tudo é feito através deste mecanismo de pesquisa.", + "title": "De onde vem o conteúdo?" + }, + "q2": { + "body": "Não é possível solicitar um programa ou filme, o sudo-flix não gere nenhum conteúdo. Todo o conteúdo é visualizado através de fontes na internet.", + "title": "Onde posso solicitar um programa ou filme?" + }, + "q3": { + "body": "Os nossos resultados de pesquisa são alimentados pela The Movie Database (TMDB) e são exibidos independentemente de as nossas fontes realmente terem o conteúdo.", + "title": "Os resultados da pesquisa mostram o programa ou filme, por que não consigo reproduzi-lo?" + }, + "title": "Sobre o sudo-flix" + }, + "actions": { + "copied": "Copiado", + "copy": "Copiar" + }, + "auth": { + "createAccount": "Ainda não tem uma conta? <0>Crie uma conta.", + "deviceNameLabel": "Nome do dispositivo", + "deviceNamePlaceholder": "Telemóvel pessoal", + "generate": { + "description": "A sua frase-passe age como o seu nome de utilizador e senha. Certifique-se de a manter segura, pois precisará dela para entrar na sua conta", + "next": "Guardei a minha frase-passe", + "passphraseFrameLabel": "Frase-passe", + "title": "A sua frase-passe" + }, + "hasAccount": "Já tem uma conta? <0>Entre aqui.", + "login": { + "description": "Por favor, introduza a sua frase-passe para entrar na sua conta", + "deviceLengthError": "Por favor, introduza um nome de dispositivo", + "passphraseLabel": "Frase-passe de 12 palavras", + "passphrasePlaceholder": "Frase-passe", + "submit": "Entrar", + "title": "Entrar na sua conta", + "validationError": "Frase-passe incorreta ou incompleta" + }, + "register": { + "information": { + "color1": "Cor de perfil um", + "color2": "Cor de perfil dois", + "header": "Introduza um nome para o seu dispositivo e escolha cores e um ícone de utilizador da sua escolha", + "icon": "Ícone de utilizador", + "next": "Próximo", + "title": "Informações da conta" + } + }, + "trust": { + "failed": { + "text": "Configurou corretamente?", + "title": "Falha ao conectar-se ao servidor" + }, + "host": "Está a conectar-se a <0>{{hostname}} - confirme se confia antes de criar uma conta", + "no": "Voltar", + "title": "Confia neste servidor?", + "yes": "Confio neste servidor" + }, + "verify": { + "description": "Por favor, introduza a sua frase-passe anterior para confirmar que a guardou e para criar a sua conta", + "invalidData": "Dados inválidos", + "noMatch": "A frase-passe não coincide", + "passphraseLabel": "A sua frase-passe de 12 palavras", + "recaptchaFailed": "Falha na validação do ReCaptcha", + "register": "Criar conta", + "title": "Confirmar a sua frase-passe" + } + }, + "errors": { + "badge": "Houve um erro", + "details": "Detalhes do erro", + "reloadPage": "Recarregar a página", + "showError": "Mostrar detalhes do erro", + "title": "Encontrámos um erro!" + }, + "footer": { + "legal": { + "disclaimer": "Aviso legal", + "disclaimerText": "sudo-flix não hospeda quaisquer ficheiros, apenas faz ligações para serviços de terceiros. Problemas legais devem ser tratados com os anfitriões e fornecedores de ficheiros. O sudo-flix não é responsável por quaisquer ficheiros multimédia mostrados pelos fornecedores de vídeo." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Assista aos seus programas e filmes favoritos com esta aplicação de streaming de código aberto." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Sobre", + "dmca": "DMCA", + "login": "Entrar", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registrar", + "settings": "Configurações" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Marcadores" + }, + "continueWatching": { + "sectionTitle": "Continuar a assistir" + }, + "mediaList": { + "stopEditing": "Parar de editar" + }, + "search": { + "allResults": "É tudo o que temos!", + "failed": "Falha ao encontrar mídia, tente novamente!", + "loading": "A carregar...", + "noResults": "Não conseguimos encontrar nada!", + "placeholder": "O que deseja assistir?", + "sectionTitle": "Resultados da pesquisa" + }, + "titles": { + "day": { + "default": "O que gostaria de assistir esta tarde?", + "extra": [ + "Sentindo-se aventureiro? Jurassic Park pode ser a escolha perfeita." + ] + }, + "morning": { + "default": "O que gostaria de assistir esta manhã?", + "extra": ["Dizem que Antes do Amanhecer é bom"] + }, + "night": { + "default": "O que gostaria de assistir esta noite?", + "extra": ["Cansado? Dizem que O Exorcista é bom."] + } + } + }, + "media": { + "episodeDisplay": "T{{season}} E{{episode}}", + "types": { + "movie": "Filme", + "show": "Série" + } + }, + "navigation": { + "banner": { + "offline": "Verifique a sua conexão à internet" + }, + "menu": { + "about": "Sobre nós", + "donation": "Doar", + "logout": "Sair", + "register": "Sincronizar com a nuvem", + "settings": "Configurações", + "support": "Suporte" + } + }, + "notFound": { + "badge": "Não encontrado", + "goHome": "Voltar para casa", + "message": "Procurámos em todo lugar: embaixo dos caixotes, no armário, atrás do proxy, mas, no final, não conseguimos encontrar a página que procura.", + "title": "Não foi possível encontrar essa página" + }, + "overlays": { + "close": "Fechar" + }, + "player": { + "back": { + "default": "Voltar para casa", + "short": "Voltar" + }, + "casting": { + "enabled": "Transmitindo para o dispositivo..." + }, + "menus": { + "downloads": { + "disclaimer": "Os downloads são feitos diretamente pelo fornecedor. O sudo-flix não tem controle sobre como os downloads são fornecidos.", + "downloadPlaylist": "Baixar lista de reprodução", + "downloadSubtitle": "Baixar legenda atual", + "downloadVideo": "Baixar vídeo", + "hlsDisclaimer": "Os downloads são feitos diretamente pelo fornecedor. O sudo-flix não tem controle sobre como os downloads são fornecidos. Por favor, note que está a baixar uma lista de reprodução HLS, isso é destinado a utilizadores familiarizados com streaming multimídia avançado.", + "onAndroid": { + "1": "Para baixar no Android, clique no botão de download e, na nova página, toque e segure no vídeo, depois selecione guardar.", + "shortTitle": "Baixar / Android", + "title": "Baixando no Android" + }, + "onIos": { + "1": "Para baixar no iOS, clique no botão de download e, na nova página, clique em , depois em Guardar no Ficheiro .", + "shortTitle": "Baixar / iOS", + "title": "Baixando no iOS" + }, + "onPc": { + "1": "No PC, clique no botão de download e, na nova página, clique com o botão direito no vídeo e selecione Guardar vídeo como", + "shortTitle": "Baixar / PC", + "title": "Baixando no PC" + }, + "title": "Baixar" + }, + "episodes": { + "button": "Episódios", + "emptyState": "Não há episódios nesta temporada, volte mais tarde!", + "episodeBadge": "E{{episode}}", + "loadingError": "Erro ao carregar a temporada", + "loadingList": "A carregar...", + "loadingTitle": "A carregar...", + "unairedEpisodes": "Um ou mais episódios nesta temporada foram desativados porque ainda não foram transmitidos." + }, + "playback": { + "speedLabel": "Velocidade de reprodução", + "title": "Configurações de reprodução" + }, + "quality": { + "automaticLabel": "Qualidade automática", + "hint": "Pode tentar <0>mudar de fonte para obter opções de qualidade diferentes.", + "iosNoQuality": "Devido a limitações definidas pela Apple, a seleção de qualidade não está disponível no iOS para esta fonte. Pode tentar <0>mudar para outra fonte para obter opções de qualidade diferentes.", + "title": "Qualidade" + }, + "settings": { + "downloadItem": "Download", + "enableSubtitles": "Ativar legendas", + "experienceSection": "Experiência de visualização", + "playbackItem": "Configurações de reprodução", + "qualityItem": "Qualidade", + "sourceItem": "Fontes de vídeo", + "subtitleItem": "Configurações de legendas", + "videoSection": "Configurações de vídeo" + }, + "sources": { + "failed": { + "text": "Houve um erro ao tentar encontrar vídeos, por favor, tente uma fonte diferente.", + "title": "Falha ao obter" + }, + "noEmbeds": { + "text": "Não conseguimos encontrar nenhum embed, por favor, tente uma fonte diferente.", + "title": "Nenhum embed encontrado" + }, + "noStream": { + "text": "Esta fonte não tem transmissões para este filme ou série.", + "title": "Sem transmissão" + }, + "title": "Fontes", + "unknownOption": "Desconhecido" + }, + "subtitles": { + "customChoice": "Selecionar legenda do arquivo", + "customizeLabel": "Personalizar", + "offChoice": "Desativar", + "settings": { + "backlink": "Legendas personalizadas", + "delay": "Atraso das legendas", + "fixCapitals": "Corrigir maiúsculas" + }, + "title": "Legendas", + "unknownLanguage": "Desconhecido" + } + }, + "metadata": { + "api": { + "text": "Não foi possível carregar os metadados da API, por favor verifique a sua conexão à internet.", + "title": "Falha ao carregar os metadados da API" + }, + "failed": { + "badge": "Falhou", + "homeButton": "Ir para casa", + "text": "Não foi possível carregar os metadados do media da TMDB. Por favor, verifique se a TMDB está indisponível ou bloqueada na sua conexão à internet.", + "title": "Falha ao carregar os metadados" + }, + "notFound": { + "badge": "Não encontrado", + "homeButton": "Voltar para casa", + "text": "Não conseguimos encontrar o conteúdo que solicitou. Ou foi removido ou houve manipulação na URL.", + "title": "Não foi possível encontrar esse conteúdo." + } + }, + "nextEpisode": { + "cancel": "Cancelar", + "next": "Próximo episódio" + }, + "playbackError": { + "badge": "Erro de reprodução", + "errors": { + "errorAborted": "A recuperação do conteúdo foi cancelada a pedido do utilizador.", + "errorDecode": "Apesar de ter sido anteriormente considerado utilizável, ocorreu um erro ao tentar decodificar o recurso multimédia, resultando em um erro.", + "errorGenericMedia": "Ocorreu um erro desconhecido no multimédia.", + "errorNetwork": "Ocorreu algum tipo de erro de rede que impediu a recuperação bem-sucedida do multimédia, apesar de estar disponível anteriormente.", + "errorNotSupported": "O objeto multimédia ou do fornecedor de multimédia não é suportado." + }, + "homeButton": "Ir para casa", + "text": "Ocorreu um erro ao tentar reproduzir o conteúdo multimédia. Por favor, tente novamente.", + "title": "Falha ao reproduzir o vídeo!" + }, + "scraping": { + "items": { + "failure": "Ocorreu um erro", + "notFound": "Não possui o vídeo", + "pending": "A verificar vídeos..." + }, + "notFound": { + "badge": "Não encontrado", + "detailsButton": "Mostrar detalhes", + "homeButton": "Ir para casa", + "text": "Pesquisámos pelos nossos fornecedores e não conseguimos encontrar o conteúdo que procura! Não alojamos o conteúdo multimédia e não temos controlo sobre o que está disponível. Por favor, clique em 'Mostrar detalhes' abaixo para mais informações.", + "title": "Não conseguimos encontrar isso" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} restantes • Termina às {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Por favor, verifique que é humano completando o Captcha à direita. Isso é para manter o sudo-flix seguro!", + "error": "Falha ao verificar a sua humanidade. Por favor, tente novamente.", + "title": "Precisamos verificar que você é humano.", + "verifyingHumanity": "Verificando a sua humanidade..." + } + }, + "screens": { + "dmca": { + "text": "Bem-vindo à página de contacto DMCA da sudo-flix! Respeitamos os direitos de propriedade intelectual e queremos resolver rapidamente quaisquer preocupações de direitos autorais. Se acredita que a sua obra protegida por direitos autorais foi usada indevidamente na nossa plataforma, envie um aviso DMCA detalhado para o email abaixo. Inclua uma descrição do material protegido por direitos autorais, os seus detalhes de contacto e uma declaração de boa fé. Comprometemo-nos a resolver essas questões prontamente e agradecemos a sua cooperação para manter a sudo-flix como um lugar que respeita a criatividade e os direitos autorais.", + "title": "DMCA" + }, + "loadingApp": "A carregar a aplicação", + "loadingUser": "A carregar o seu perfil", + "loadingUserError": { + "logout": "Terminar sessão", + "reset": "Repor servidor personalizado", + "text": "Falha ao carregar o seu perfil", + "textWithReset": "Falha ao carregar o seu perfil do servidor personalizado. Deseja repor para o servidor padrão?" + }, + "migration": { + "failed": "Falha ao migrar os seus dados.", + "inProgress": "Por favor aguarde, estamos a migrar os seus dados. Isto não deverá demorar muito." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Nome do dispositivo", + "deviceNamePlaceholder": "Telemóvel pessoal", + "editProfile": "Editar", + "logoutButton": "Terminar sessão" + }, + "actions": { + "delete": { + "button": "Eliminar conta", + "confirmButton": "Eliminar conta", + "confirmDescription": "Tem a certeza de que deseja eliminar a sua conta? Todos os seus dados serão perdidos!", + "confirmTitle": "Tem a certeza?", + "text": "Esta ação é irreversível. Todos os dados serão eliminados e nada poderá ser recuperado.", + "title": "Eliminar conta" + }, + "title": "Ações" + }, + "devices": { + "deviceNameLabel": "Nome do dispositivo", + "failed": "Falha ao carregar as sessões", + "removeDevice": "Remover", + "title": "Dispositivos" + }, + "profile": { + "finish": "Terminar edição", + "firstColor": "Cor do perfil um", + "secondColor": "Cor do perfil dois", + "title": "Editar imagem de perfil", + "userIcon": "Ícone de utilizador" + }, + "register": { + "cta": "Começar", + "text": "Partilhe o seu progresso de visualização entre dispositivos e mantenha-os sincronizados.", + "title": "Sincronizar com a nuvem" + }, + "title": "Conta" + }, + "appearance": { + "activeTheme": "Ativo", + "themes": { + "blue": "Azul", + "default": "Padrão", + "gray": "Cinzento", + "red": "Vermelho", + "teal": "Verde-azulado" + }, + "title": "Aparência" + }, + "connections": { + "server": { + "description": "Se desejar ligar a um servidor personalizado para armazenar os seus dados, ative isto e forneça o URL.", + "label": "Servidor personalizado", + "urlLabel": "URL do servidor personalizado" + }, + "title": "Conexões", + "workers": { + "addButton": "Adicionar novo trabalhador", + "description": "Para que a aplicação funcione, todo o tráfego é encaminhado através de proxies. Ative isto se quiser utilizar os seus próprios trabalhadores.", + "emptyState": "Ainda não há trabalhadores, adicione um abaixo", + "label": "Utilizar trabalhadores de proxy personalizados", + "urlLabel": "URLs do trabalhador", + "urlPlaceholder": "https://" + } + }, + "reset": "Repor", + "save": "Guardar", + "sidebar": { + "info": { + "appVersion": "Versão da aplicação", + "backendUrl": "URL do backend", + "backendVersion": "Versão do backend", + "hostname": "Hostname", + "insecure": "Inseguro", + "notLoggedIn": "Não está autenticado", + "secure": "Seguro", + "title": "Informações da aplicação", + "unknownVersion": "Desconhecida", + "userId": "ID de utilizador" + } + }, + "subtitles": { + "backgroundLabel": "Opacidade do fundo", + "colorLabel": "Cor", + "previewQuote": "Não devo temer. O medo é o assassino da mente.", + "textSizeLabel": "Tamanho do texto", + "title": "Legendas" + }, + "unsaved": "Tem alterações não guardadas" + } +} diff --git a/src/assets/locales/ro.json b/src/assets/locales/ro.json new file mode 100644 index 00000000..d26c986c --- /dev/null +++ b/src/assets/locales/ro.json @@ -0,0 +1,444 @@ +{ + "about": { + "description": "sudo-flix este o aplicație web care caută fluxuri pe internet. Echipa urmărește o abordare mai ales minimalistă a consumului de conținut.", + "faqTitle": "Întrebări obișnuite", + "q1": { + "body": "sudo-flix nu găzduiește niciun conținut. Când faceți clic pe ceva pentru a viziona, pe Internet este căutat media selectată (Pe ecranul de încărcare și în fila „Surse video”, puteți vedea ce sursă utilizați). Media nu este niciodată încărcată de sudo-flix, totul se face prin acest mecanism de căutare.", + "title": "De unde vine conținutul?" + }, + "q2": { + "body": "Nu este posibil să solicitați o emisiune sau un film, sudo-flix nu gestionează niciun conținut. Tot conținutul este vizualizat prin surse de internet.", + "title": "Unde pot solicita o emisiune sau un film?" + }, + "q3": { + "body": "Rezultatele căutării noastre sunt furnizate de The Movie Database (TMDB) și afișați indiferent dacă sursele noastre au de fapt conținutul.", + "title": "Rezultatele căutării afișează emisiunea sau filmul, de ce nu îl pot reda?" + }, + "title": "Despre sudo-flix" + }, + "actions": { + "copied": "Copiat", + "copy": "Copiați" + }, + "auth": { + "createAccount": "Nu aveți încă un cont? <0>Creați un cont.", + "deviceNameLabel": "Nume dispozitiv", + "deviceNamePlaceholder": "Telefon personal", + "generate": { + "description": "Fraza de acces acționează ca nume de utilizator și parolă. Asigurați-vă că îl păstrați în siguranță, deoarece va trebui să îl introduceți pentru a vă conecta la contul dvs", + "next": "Mi-am salvat fraza de acces", + "passphraseFrameLabel": "Frază de acces", + "title": "Fraza dvs. de acces" + }, + "hasAccount": "ai deja un cont? <0>Autentificați-vă aici.", + "login": { + "description": "Vă rugăm să introduceți fraza de acces pentru a vă conecta la contul dvs", + "deviceLengthError": "Introduceți un nume de dispozitiv", + "passphraseLabel": "Expresie de acces din 12 cuvinte", + "passphrasePlaceholder": "Frază de acces", + "submit": "Conectează te", + "title": "conecteaza-te la contul tau", + "validationError": "Fraza de acces este incorectă sau incompletă" + }, + "register": { + "information": { + "color1": "Culoarea profilului unu", + "color2": "Culoarea profilului doi", + "header": "Introduceți un nume pentru dispozitivul dvs. și alegeți culorile și o pictogramă de utilizator la alegerea dvs", + "icon": "Pictograma utilizatorului", + "next": "Următorul", + "title": "Informații despre cont" + } + }, + "trust": { + "failed": { + "text": "L-ai configurat corect?", + "title": "Nu s-a putut ajunge la server" + }, + "host": "Vă conectați la <0>{{hostname}} - vă rugăm să confirmați că aveți încredere înainte de a vă crea un cont", + "no": "Întoarce-te", + "title": "Ai încredere în acest server?", + "yes": "Am încredere în acest server" + }, + "verify": { + "description": "Introduceți expresia de acces de mai devreme pentru a confirma că ați salvat-o și pentru a vă crea contul", + "invalidData": "Datele nu sunt valide", + "noMatch": "Fraza de acces nu se potrivește", + "passphraseLabel": "Fraza dvs. de acces de 12 cuvinte", + "recaptchaFailed": "Validarea ReCaptcha a eșuat", + "register": "Creează cont", + "title": "Confirmați-vă fraza de acces" + } + }, + "errors": { + "badge": "S-a spart", + "details": "Detalii despre eroare", + "reloadPage": "Reîncărcați pagina", + "showError": "Afișați detalii despre eroare", + "title": "Am intampinat o eroare!" + }, + "footer": { + "legal": { + "disclaimer": "Disclaimer", + "disclaimerText": "sudo-flix nu găzduiește niciun fișier, ci doar trimite la servicii terțe. Problemele juridice ar trebui abordate cu gazdele și furnizorii de fișiere. sudo-flix nu este responsabil pentru niciun fișier media afișat de furnizorii de video." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Urmăriți emisiunile și filmele preferate cu această aplicație de streaming open source." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Despre", + "dmca": "DMCA", + "login": "Log in", + "pagetitle": "{{title}} - sudo-flix", + "register": "Inregistreaza-te", + "settings": "Setări" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Marcaje" + }, + "continueWatching": { + "sectionTitle": "Continuați vizionarea" + }, + "mediaList": { + "stopEditing": "Opriți editarea" + }, + "search": { + "allResults": "Asta e tot ce avem!", + "failed": "Găsire media eșuată, încearcă din nou!", + "loading": "Se încarcă...", + "noResults": "Nu am putut găsi nimic!", + "placeholder": "La ce dorești să te uiți?", + "sectionTitle": "Rezultate de căutare" + }, + "titles": { + "day": { + "default": "La ce vrei să te uiți după-amiaza asta?", + "extra": [ + "Te simți aventuros? Jurassic Park ar putea fi o alegere perfectă." + ] + }, + "morning": { + "default": "La ce dorești să te in uiți dimineață aceasta?", + "extra": ["Aud că Before Sunrise este bun"] + }, + "night": { + "default": "La ce dorești să te uiți în astă seară?", + "extra": ["Obosit? Aud că The Exorcist is good."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Spectacol" + } + }, + "navigation": { + "banner": { + "offline": "Verificați-vă conexiunea de internet" + }, + "menu": { + "about": "Despre noi", + "donation": "Donează", + "logout": "Deconectați-vă", + "register": "Sincronizare în cloud", + "settings": "Setări", + "support": "Ajutor" + } + }, + "notFound": { + "badge": "Nu a fost găsit", + "goHome": "Înapoi acasă", + "message": "Ne-am uitat peste tot: sub pubele, în dulap, În spatele proxy-ului dar din păcate nu am găsit pagina pe care dumneavoastră o căutați.", + "title": "N-am putut găsi pagina" + }, + "overlays": { + "close": "Închide" + }, + "player": { + "back": { + "default": "Înapoi acasă", + "short": "Înapoi" + }, + "casting": { + "enabled": "Casting pe dispozitiv..." + }, + "menus": { + "downloads": { + "disclaimer": "Descărcările sunt luate direct de la furnizor. sudo-flix nu are control cum descărcările sunt furnizate.", + "downloadPlaylist": "Descarcă lista de redare", + "downloadSubtitle": "Descărcați subtitlul curente", + "downloadVideo": "Descarcă video", + "hlsDisclaimer": "Descărcările sunt preluate direct de la furnizor. sudo-flix nu are control asupra modului în care sunt furnizate descărcările. Vă rugăm să rețineți că descărcați o listă de redare HLS, aceasta este destinată utilizatorilor familiar cu streamingul avansat.", + "onAndroid": { + "1": "Pentru a descărca pe android, apăsați butonul de descărcare apoi în pagina nouă,apăsați și țineți pe video, apoi selectați salvează.", + "shortTitle": "Descărcare / Android", + "title": "Descărcare pe Android" + }, + "onIos": { + "1": "Pentru a instala pe iOS, apăsați butonul de instalare apoi în pagina nouă apăsați , apoi Salvează in File .", + "shortTitle": "Descărcare / iOS", + "title": "Descărcare pe iOS" + }, + "onPc": { + "1": "Pe calculator, apăsați butonul de descărcare apoi în pagina nouă, apăsați click drept pe video și selectați Salvați videoul ca", + "shortTitle": "Descarcă / Calculator", + "title": "Descărcare pe calculator" + }, + "title": "Descarcă" + }, + "episodes": { + "button": "Episoade", + "emptyState": "Nu sunt episoade in sezonul acesta, reveniți mai târziu!", + "episodeBadge": "E{{episode}}", + "loadingError": "Eroare la încărcarea sezonul", + "loadingList": "Se încarcă...", + "loadingTitle": "Se încarcă...", + "unairedEpisodes": "Unul sau mai multe episoade din sezonul acesta sunt indisponibile deoarece incă nu au venit încă." + }, + "playback": { + "speedLabel": "Viteză de redare", + "title": "Setările de redare" + }, + "quality": { + "automaticLabel": "Calitate automată", + "hint": "Puteți încerca <0>Schimbarea sursei pentru a primi opțiuni de calitate diferită.", + "iosNoQuality": "Din cauza limitărilor definite de Apple, selecția calității nu este disponibilă pe iOS pentru această sursă. Puteți încerca să <0>treceți la altă sursă pentru a obține diferite opțiuni de calitate.", + "title": "Calitate" + }, + "settings": { + "downloadItem": "Descarcă", + "enableSubtitles": "Activează subtitlurile", + "experienceSection": "Experiență de vizionare", + "playbackItem": "Setări de redare", + "qualityItem": "Calitate", + "sourceItem": "Surse video", + "subtitleItem": "Setările subtitlului", + "videoSection": "Setări video" + }, + "sources": { + "failed": { + "text": "Aici a fost o eroare încercând să găsim video, încercați o altă sursă.", + "title": "Nu s-a răzuit" + }, + "noEmbeds": { + "text": "Nu am putut găsi nicio incorporare, vă rog să încercați o altă sursă.", + "title": "Nu a fost găsită nicio încorporare" + }, + "noStream": { + "text": "Sursa asta nu are nicio sursă de streaming pentru filmul său spectacolul.", + "title": "Niciun stream" + }, + "title": "Surse", + "unknownOption": "Necunoscut" + }, + "subtitles": { + "customChoice": "Selectați subtitlul din filă", + "customizeLabel": "Costumizează", + "offChoice": "Oprit", + "settings": { + "backlink": "Subtitluri personalizate", + "delay": "Intârziera subtitlurilor", + "fixCapitals": "Fixați capitalizația" + }, + "title": "Subtitluri", + "unknownLanguage": "Necunoscut" + } + }, + "metadata": { + "api": { + "text": "Metadatele API nu au putut fi încărcate, vă rugăm să vă verificați conexiunea la internet.", + "title": "Nu s-a putut incărca API metadata" + }, + "dmca": { + "badge": "Șters", + "text": "Această medie nu mai este valabilă din vina copyright-ului.", + "title": "Media a fost ștearsă" + }, + "extensionPermission": { + "badge": "Permisune pierdută", + "button": "Folosește extensia", + "text": "Tu ai extensia, dar noi avem nevoie de permisiune ca să începem să folosim extensia.", + "title": "Configurează extensia" + }, + "failed": { + "badge": "Eșuat", + "homeButton": "Du-te acasă :)", + "text": "Nu s-au putut încărca metadatele media din TMDB. Vă rugăm să verificați dacă TMDB este oprit sau blocat pe conexiunea ta. la internet.", + "title": "Nu s-au încărcat metadatele" + }, + "notFound": { + "badge": "Nu a fost găsit", + "homeButton": "Înapoi acasă :)", + "text": "Nu am putut găsi media pe care ați cerut-o. Poate este ștearsă sau ați manipulat adresa URL.", + "title": "Nu am putut găsi acel media." + } + }, + "nextEpisode": { + "cancel": "Anulare", + "next": "Episodul următor" + }, + "playbackError": { + "badge": "Eroare la redare", + "errors": { + "errorAborted": "Preluarea media a fost întreruptă la cererea utilizatorului.", + "errorDecode": "În ciuda faptului că anterior a fost determinată a fi utilizabilă, a apărut o eroare în timpul încercării de a decoda resursa media, rezultând o eroare.", + "errorGenericMedia": "Eroare media cumva sa întâmplat.", + "errorNetwork": "A apărut o eroare de rețea care a împiedicat preluarea cu succes a suportului media, în ciuda faptului că fusese disponibil anterior.", + "errorNotSupported": "Obiectul media sau furnizorul media nu este acceptat." + }, + "homeButton": "Du-te acasă :)", + "text": "A apărut o eroare la încercarea de a reda media. Vă rugăm să încercați din nou.", + "title": "O eroare la redarea videoului!" + }, + "scraping": { + "items": { + "failure": "Sa întâmplat o eraore", + "notFound": "Nu are acel video", + "pending": "Verificând pentru videouri..." + }, + "notFound": { + "badge": "Nu a fost găsit", + "detailsButton": "Arată detalii", + "homeButton": "Du-te acasă", + "text": "Am căutat prin furnizorii noștri și nu putem găsi media pe care o căutați! Nu găzduim mass-media și nu avem control asupra a ceea ce este disponibil. Vă rugăm să faceți clic pe „Afișați detalii” de mai jos pentru mai multe detalii.", + "title": "Nu am putut găsi aia" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} rămas • Se termină la{{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Vă rog să vă verificați umanitatea completând acest captcha in partea dreapta. Asta este pentru a pastra sudo-flix sigur!", + "error": "Verificarea umanității tale a eșuat. Încercați din nou.", + "title": "Noi trebuie să verificăm dacă sunteți un om.", + "verifyingHumanity": "Verificați-vă umanitatea voastră..." + } + }, + "screens": { + "dmca": { + "text": "Bun venit pe pagina de contact DMCA a sudo-flix! Respectăm drepturile de proprietate intelectuală și dorim să abordăm rapid orice problemă privind drepturile de autor. Dacă credeți că lucrarea dumneavoastră protejată prin drepturi de autor a fost utilizată necorespunzător pe platforma noastră, vă rugăm să trimiteți o notificare detaliată DMCA la e-mailul de mai jos. Vă rugăm să includeți o descriere a materialului protejat prin drepturi de autor, detaliile dvs. de contact și o declarație de bună credință. Ne angajăm să rezolvăm aceste probleme cu promptitudine și apreciem cooperarea dumneavoastră pentru a menține sudo-flix un loc care respectă creativitatea și drepturile de autor.", + "title": "DMCA" + }, + "loadingApp": "Se încarcă aplicația", + "loadingUser": "Se încarcă profilul tău", + "loadingUserError": { + "logout": "Deconectare", + "reset": "Resetare servere personalizate", + "text": "Nu s-a încărcat profilul dvs", + "textWithReset": "Nu s-a încărcat profilul dvs. de pe serverul personalizat, doriți să reveniți la serverul implicit?" + }, + "migration": { + "failed": "Eroare la migrarea datelor voastre.", + "inProgress": "Vă rugăm să așteptați, migrăm datele dvs. Acest lucru nu ar trebui să dureze mult." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Numele dispozitivului", + "deviceNamePlaceholder": "Telefonul personal", + "editProfile": "Editează", + "logoutButton": "Deconectați-vă" + }, + "actions": { + "delete": { + "button": "Șterge contul", + "confirmButton": "Șterge contul", + "confirmDescription": "Ești sigur că vrei să ștergi contul tău? Toate datele tale vor dispărea!", + "confirmTitle": "Ești sigur?", + "text": "Acțiunea asta este ireversibilă. Toate datele tale vor dispărea și nimic nu va putea fi recuperat.", + "title": "Șterge contul" + }, + "title": "Acțiuni" + }, + "devices": { + "deviceNameLabel": "Numele dispozitivului", + "failed": "Eroare la încărcarea sesiunii", + "removeDevice": "Șterge", + "title": "Dispozitive" + }, + "profile": { + "finish": "Termină editarea", + "firstColor": "Culoarea profilului unu", + "secondColor": "Culoare profilului doi", + "title": "Editează poza de profil", + "userIcon": "Iconița utilizatorului" + }, + "register": { + "cta": "Începeți", + "text": "Partajați progresul vizionării între dispozitive și păstrați-le sincronizate.", + "title": "Sincronizare în cloud" + }, + "title": "Cont" + }, + "appearance": { + "activeTheme": "Activ", + "themes": { + "blue": "Albastru", + "default": "Implicit", + "gray": "Gri", + "red": "Roșu", + "teal": "Albastru verziu" + }, + "title": "Aparență" + }, + "connections": { + "server": { + "description": "Dacă doriți să vă conectați la un backend personalizat pentru a vă stoca datele, activați acest lucru și furnizați adresa URL.", + "label": "Server personalizat", + "urlLabel": "Adresa URL personalizată a serverului" + }, + "setup": { + "errorStatus": { + "title": "Ceva are nevoie de atenția ta" + } + }, + "title": "Conexiuni", + "workers": { + "addButton": "Adaugă un nou muncitor", + "description": "Pentru ca aplicația să funcționeze, tot traficul este direcționat prin proxy. Activați acest lucru dacă doriți să vă aduceți proprii lucrători.", + "emptyState": "Niciun muncitor incă, adaugă unu", + "label": "Utilizați lucrători proxy personalizați", + "urlLabel": "Adresele URL ale lucrătorilor", + "urlPlaceholder": "https://" + } + }, + "reset": "Resetare", + "save": "Salvează", + "sidebar": { + "info": { + "appVersion": "Versiunea aplicației", + "backendUrl": "URL de backend", + "backendVersion": "Versiunea backend", + "hostname": "Numele gazdei", + "insecure": "Nesigur", + "notLoggedIn": "Nu sunteți autentificat", + "secure": "Securizat", + "title": "Informația aplicației", + "unknownVersion": "Necunoscut", + "userId": "ID-ul utilizatorului" + } + }, + "subtitles": { + "backgroundLabel": "Opacitatea fundalului", + "colorLabel": "Culoare", + "previewQuote": "nu trebuie să mă tem. Frica este ucigașul minții.", + "textSizeLabel": "Dimensiunea textului", + "title": "Subtitluri" + }, + "unsaved": "Aveți modificări nesalvate" + } +} diff --git a/src/assets/locales/ru.json b/src/assets/locales/ru.json new file mode 100644 index 00000000..1bb1f7d6 --- /dev/null +++ b/src/assets/locales/ru.json @@ -0,0 +1,315 @@ +{ + "about": { + "description": "sudo-flix - это веб-приложение, которое ищет в интернете потоки. Команда стремится к минималистичному подходу к потреблению контента.", + "faqTitle": "Общие вопросы", + "q1": { + "body": "sudo-flix не размещает у себя никакого контента. Когда вы нажимаете на что-то для просмотра, в интернете происходит поиск выбранного медиа файла (на экране загрузки и во вкладке \"Видео источники\" вы можете увидеть, какой источник вы используете). Медиа файлы никогда не загружается на sudo-flix, все происходит через этот механизм поиска.", + "title": "Откуда берётся контент?" + }, + "q2": { + "body": "Невозможно запросить сериал или фильм, sudo-flix не управляет никаким контентом. Весь контент просматривается через источники в интернете.", + "title": "Где я могу запросить показ сериала или фильма?" + }, + "q3": { + "body": "Наши результаты поиска основаны на базе данных The Movie Database (TMDB) и отображаются вне зависимости от того, есть ли в наших источниках соответствующий контент.", + "title": "В результатах поиска отображается сериал или фильм, но почему я не могу воспроизвести его?" + }, + "title": "О sudo-flix" + }, + "actions": { + "copied": "Скопировано", + "copy": "Копировать" + }, + "auth": { + "createAccount": "У вас ещё нет аккаунта? <0>Создайте учётную запись.", + "deviceNameLabel": "Имя устройства", + "deviceNamePlaceholder": "Личный телефон", + "generate": { + "description": "Твоя фраза работает как твой пароль и имя. Сделай чтобы она была в защите чтобы ты смог зайти снова в аккаунт", + "next": "Я сохранил мою фразу", + "passphraseFrameLabel": "Фраза", + "title": "Твоя фраза" + }, + "hasAccount": "У вас уже есть аккаунт? <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}} - пожалуйста, подтвердите, что вы доверяете ему, прежде чем создавать учётную запись", + "no": "Вернуться назад", + "title": "Доверяете ли вы этому серверу?", + "yes": "Я доверяю этому серверу" + }, + "verify": { + "description": "Пожалуйста, введите фразу, полученную ранее, чтобы подтвердить, что вы ее сохранили, и создать свой аккаунт", + "invalidData": "Дата инвалидная", + "noMatch": "Парольная фраза не совпадает", + "passphraseLabel": "Ваша 12-словная парольная фраза", + "recaptchaFailed": "Проверка ReCaptcha не удалась", + "register": "Создать учётную запись", + "title": "Подтвердите парольную фразу" + } + }, + "errors": { + "details": "Подробности ошибки", + "reloadPage": "Перезагрузить страницу", + "showError": "Показать сведения об ошибке", + "title": "Мы столкнулись с ошибкой!" + }, + "footer": { + "legal": { + "disclaimer": "Отказ от ответственности", + "disclaimerText": "sudo-flix не размещает никаких файлов, а лишь предоставляет ссылки на сторонние сервисы. Юридические вопросы следует решать с владельцами файлов и поставщиками услуг. sudo-flix не несёт ответственности за любые медиа файлы, показанные поставщиками видео." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Смотрите любимые сериалы и фильмы с помощью этого приложения для потокового вещания с открытым исходным кодом." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "О", + "dmca": "DMCA", + "pagetitle": "{{title}} - sudo-flix", + "settings": "Настройки" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Закладки" + }, + "continueWatching": { + "sectionTitle": "Продолжить просмотр" + }, + "mediaList": { + "stopEditing": "Завершить редактирование" + }, + "search": { + "allResults": "Это все, что у нас есть!", + "loading": "Загрузка...", + "sectionTitle": "Результаты поиска" + } + }, + "media": { + "episodeDisplay": "С{{season}} Э{{episode}}", + "types": { + "movie": "Фильм", + "show": "Сериал" + } + }, + "navigation": { + "banner": { + "offline": "Проверьте подключение к Интернету" + }, + "menu": { + "about": "О нас", + "donation": "Пожертвовать", + "logout": "Выйти", + "settings": "Настройки", + "support": "Поддержка" + } + }, + "notFound": { + "title": "Не удалось найти эту страницу" + }, + "overlays": { + "close": "Закрыть" + }, + "player": { + "back": { + "default": "Вернуться на главную" + }, + "menus": { + "downloads": { + "downloadSubtitle": "Скачать текущие субтитры", + "title": "Скачать" + }, + "episodes": { + "button": "Эпизоды", + "emptyState": "В этом сезоне нет ни одной серии, загляните позже!", + "episodeBadge": "Э{{episode}}", + "loadingError": "Ошибка при загрузке сезона", + "loadingList": "Загрузка...", + "loadingTitle": "Загрузка..." + }, + "playback": { + "speedLabel": "Скорость воспроизведения", + "title": "Настройки воспроизведения" + }, + "quality": { + "automaticLabel": "Автоматическое качество" + }, + "settings": { + "downloadItem": "Скачать", + "enableSubtitles": "Включить субтитры", + "playbackItem": "Настройки воспроизведения", + "qualityItem": "Качество", + "sourceItem": "Видео источники", + "subtitleItem": "Настройки субтитров", + "videoSection": "Настройки видео" + }, + "sources": { + "failed": { + "text": "При попытке найти видео произошла ошибка, пожалуйста, попробуйте использовать другой источник." + }, + "noEmbeds": { + "text": "Мы не смогли найти ни одной вставки, пожалуйста, попробуйте использовать другой источник.", + "title": "Не найдено ни одной вставки" + }, + "noStream": { + "text": "В этом источнике нет потоков для этого фильма или сериала.", + "title": "Нет потока" + }, + "title": "Источники", + "unknownOption": "Неизвестный" + }, + "subtitles": { + "customChoice": "Выбрать субтитры из файла", + "offChoice": "Выключить", + "settings": { + "backlink": "Пользовательские субтитры" + }, + "title": "Субтитры" + } + }, + "nextEpisode": { + "cancel": "Отмена", + "next": "След. эпизод" + }, + "playbackError": { + "badge": "Ошибка воспроизведения", + "errors": { + "errorDecode": "Несмотря на то, что ранее этот медиаресурс был пригодным для использования, при попытке его декодирования произошла ошибка." + }, + "text": "При попытке воспроизвести медиа файл произошла ошибка. Пожалуйста, попробуйте ещё раз.", + "title": "Не удалось воспроизвести видео!" + }, + "scraping": { + "notFound": { + "detailsButton": "Показать детали", + "title": "Мы не смогли найти" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}" + } + }, + "screens": { + "dmca": { + "title": "DMCA" + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Имя устройства", + "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": "Cиний", + "default": "Стандартный", + "gray": "Серый", + "red": "Красный", + "teal": "Бирюзовый" + }, + "title": "Внешний вид" + }, + "connections": { + "server": { + "description": "Если вы хотите подключиться к пользовательскому серверу для хранения ваших данных, включите эту опцию и укажите URL.", + "label": "Пользовательский сервер", + "urlLabel": "URL-адрес пользовательского сервера" + }, + "title": "Соединения", + "workers": { + "addButton": "Добавить новый прокси-сервер", + "description": "Для работы приложения весь трафик маршрутизируется через прокси. Включите это, если вы хотите использовать свои собственных прокси-серверы.", + "emptyState": "Прокси отсутствуют, добавьте их ниже", + "label": "Использовать прокси-сервера", + "urlLabel": "URL-адреса", + "urlPlaceholder": "https://" + } + }, + "reset": "Сброс", + "save": "Сохранить", + "sidebar": { + "info": { + "appVersion": "Версия приложения", + "backendUrl": "Внутренний URL-адрес", + "backendVersion": "Серверная версия", + "hostname": "Имя хоста", + "insecure": "Небезопасно", + "notLoggedIn": "Вы не авторизованы", + "secure": "Безопасный", + "title": "Информация о приложении", + "unknownVersion": "Неизвестный", + "userId": "ID пользователя" + } + }, + "subtitles": { + "backgroundLabel": "Прозрачность фона", + "colorLabel": "Цвет", + "previewQuote": "Я не должен бояться. Страх — убийца разума.", + "textSizeLabel": "Размер текста", + "title": "Субтитры" + }, + "unsaved": "У вас есть несохраненные изменения" + } +} diff --git a/src/assets/locales/sl.json b/src/assets/locales/sl.json new file mode 100644 index 00000000..e2c94b96 --- /dev/null +++ b/src/assets/locales/sl.json @@ -0,0 +1,419 @@ +{ + "about": { + "description": "sudo-flix je spletna aplikacija, ki omogoča enostavno iskanje filmov in serij. Ekipa si prizadeva za minimalističen pristop pri ogledu vsebin.", + "faqTitle": "Pogosto zastavljena vprašanja", + "q1": { + "body": "sudo-flix ne hrani nobene vsebine. Ko si želite film ali serijo ogledati, se izvede iskanje vsebine na internetu (na zaslonu za nalaganje in v zavihku \"viri videa\" lahko vidite, kateri vir uporabljate). Vsebina medija se nikoli ne naloži na strežnike sudo-flixa, ker vse poteka iskalnega mehanizma.", + "title": "Od kod prihaja vsebina?" + }, + "q2": { + "body": "Serije in filmov ni mogoče zahtevati, ker sudo-flix ne upravlja z nobeno vsebino. Vse vsebine si lahko ogledate prek virov na internetu.", + "title": "Kje lahko oddam predlog za serijo ali film?" + }, + "q3": { + "body": "Rezultati iskanja so privzeti iz podatkovne baze \"The Movie Database\" (TMDB) in se prikazujejo ne glede na to, ali je vsebina dejansko na voljo.", + "title": "Rezultati iskanja prikazujejo serijo oz. film, zakaj ga potem ne morem predvajati?" + }, + "title": "O sudo-flixbu" + }, + "actions": { + "copied": "Kopirano", + "copy": "Kopiraj" + }, + "auth": { + "createAccount": "Še nimate računa? <0>Ustvari račun.", + "deviceNameLabel": "Ime naprave", + "deviceNamePlaceholder": "Mobilni telefon", + "generate": { + "description": "Vaša fraza deluje kot vaše uporabniško ime in geslo. Shranite ga na varnem mestu, saj ga boste morali vnesti za prijavo v svoj račun", + "next": "Frazo sem shranil na varno mesto", + "passphraseFrameLabel": "Varnostna fraza", + "title": "Varnostna Fraza" + }, + "hasAccount": "Že imate račun?<0>Prijavi se tukaj.", + "login": { + "description": "Vnesite frazo za prijavo v račun", + "deviceLengthError": "Vnesi ime naprave", + "passphraseLabel": "12-besedna varnostna fraza", + "passphrasePlaceholder": "Varnostna fraza", + "submit": "Prijava", + "title": "Prijava v račun", + "validationError": "Nepravilna ali nepopolna varnostna fraza" + }, + "register": { + "information": { + "color1": "Barva prvega profila", + "color2": "Barva drugega profila", + "header": "Vnesite ime za napravo ter izberi barvo in ikono uporabnika", + "icon": "Ikona uporabnika", + "next": "Naprej", + "title": "Informacije o profilu" + } + }, + "trust": { + "failed": { + "text": "Ali ste pravilno konfigurirali nastavitve?", + "title": "Neuspešen dostop do strežnika" + }, + "host": "Povezujete se z <0>{{hostname}} - pred ustvarjanjem računa potrdite, da mu zaupate", + "no": "Nazaj", + "title": "Ali zaupate temu strežniku?", + "yes": "Zaupam strežniku" + }, + "verify": { + "description": "Vnesite varnostno frazo, ki ste ga uporabili prej, da potrdite varno hrambo, in ustvarite svoj račun", + "invalidData": "Vnešeni podatki niso pravilni", + "noMatch": "Varnostna fraza se ne ujema", + "passphraseLabel": "Vaša 12-besedna varnostna fraza", + "recaptchaFailed": "Preizkus ReCaptcha ni uspel", + "register": "Ustvari Račun", + "title": "Potrdite varnostno frazo" + } + }, + "errors": { + "badge": "Nekaj se je pokvarilo", + "details": "Podatki o napaki", + "reloadPage": "Osveži stran", + "showError": "Pokaži informacije o napaki", + "title": "Prišlo je do napake!" + }, + "footer": { + "legal": { + "disclaimer": "Opozorilo", + "disclaimerText": "sudo-flix ne gosti nobenih medijev ali datotek, temveč le povezave do storitev tretjih oseb. Pravna vprašanja je potrebno posredovati gostiteljem in ponudnikom datotek. sudo-flix ni odgovoren za medijske datoteke, ki jih prikazujejo ponudniki." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Z odprtokodno aplikacijo sudo-flix si oglejte najljubše oddaje in filme." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "O projektu", + "dmca": "DMCA", + "login": "Prijava", + "pagetitle": "{{title}} - sudo-flix", + "register": "Registriraj se", + "settings": "Nastavitve" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Zaznamki" + }, + "continueWatching": { + "sectionTitle": "Nadaljuj z ogledom" + }, + "mediaList": { + "stopEditing": "Ustavite urejanje" + }, + "search": { + "allResults": "To je vse, kar imamo na voljo!", + "failed": "Ni uspelo najti medija, prosim poskusite znova!", + "loading": "Nalaganje...", + "noResults": "Vsebin nismo našli!", + "placeholder": "Kaj si želite gledati?", + "sectionTitle": "Rezultati iskanja" + }, + "titles": { + "day": { + "default": "Kateri film ali serijo bi si želeli ogledati ob tem popoldnevu?", + "extra": [ + "Se počutite pustolovsko? Jurski park je morda odlična izbira." + ] + }, + "morning": { + "default": "Kateri film ali serijo bi si želeli ogledati ob tem jutru?", + "extra": ["Slišala sem, da je film \"Pred sončnim vzhodom\" odličen"] + }, + "night": { + "default": "Kateri film ali serijo bi si želeli ogledati nocoj?", + "extra": [ + "Utrujeni? Slišal sem, da je \"Izganjalec mrtvih\" odličen film." + ] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "Film", + "show": "Serija" + } + }, + "navigation": { + "banner": { + "offline": "Preverite vašo internetno povezavo" + }, + "menu": { + "about": "O nas", + "donation": "Doniraj", + "logout": "Odjava", + "register": "Sinhronizacija z oblakom", + "settings": "Nastavitve", + "support": "Podpora" + } + }, + "notFound": { + "badge": "Ni najdeno", + "goHome": "Nazaj domov", + "message": "Iskali smo povsod: pod preprogo, v omari, za \"proxyjem\", vendar na koncu nismo našli strani, ki jo iščete.", + "title": "Ni bilo mogoče najti te strani" + }, + "overlays": { + "close": "Zapri" + }, + "player": { + "back": { + "default": "Nazaj domov", + "short": "Nazaj" + }, + "casting": { + "enabled": "Predvajanje vsebine v napravo..." + }, + "menus": { + "downloads": { + "disclaimer": "Prenosi se opravijo neposredno pri ponudniku vsebin. sudo-flix nima nadzora nad načinom zagotavljanja prenosov.", + "downloadPlaylist": "Prenesi playlisto", + "downloadSubtitle": "Prenesi trenutne podnapise", + "downloadVideo": "Prenesi video", + "hlsDisclaimer": "Prenosi se opravijo neposredno pri ponudniku vsebin. sudo-flix nima nadzora nad načinom zagotavljanja prenosov. Upoštevajte, da prenašate seznam predvajanja tipa \"HLS\"; ta je namenjen uporabnikom, ki poznajo napredno pretakanje večpredstavnostnih vsebin.", + "onAndroid": { + "1": "Če želite prenesti v sistemu Android, kliknite gumb za prenos, nato na novi strani tipnite in držite videoposnetek ter izberite shrani.", + "shortTitle": "Prenesi / Android", + "title": "Prenos v operacijskem sistemu Android" + }, + "onIos": { + "1": "Če želite prenesti v sistemu iOS, kliknite gumb za prenos, nato na novi strani kliknite in nato Shrani v datoteke .", + "shortTitle": "Prenesu / iOS", + "title": "Prenašanje v sistemu iOS" + }, + "onPc": { + "1": "V računalniku kliknite gumb za prenos, nato na novi strani desno kliknite videoposnetek in izberite Shrani videoposnetek kot", + "shortTitle": "Prenesi / Računalnik", + "title": "Prenos na računalnik" + }, + "title": "Prenos" + }, + "episodes": { + "button": "Epizode", + "emptyState": "V tej sezoni ni epizod, oglejte si jo pozneje!", + "episodeBadge": "E{{episode}}", + "loadingError": "Napaka pri nalaganju sezone", + "loadingList": "Nalaganje...", + "loadingTitle": "Nalaganje..." + }, + "playback": { + "speedLabel": "Hitrost predvajanja", + "title": "Nastavitve predvajanja" + }, + "quality": { + "automaticLabel": "Avtomatska izbira kvalitete", + "hint": "Poskusite <0>preklop vira, da dobite različne možnosti kakovosti.", + "iosNoQuality": "Zaradi omejitev, ki jih je določil Apple, izbira kakovosti v sistemu iOS za ta vir ni na voljo. Poskusite <0>preklopiti na drug vir, da dobite različne možnosti kakovosti.", + "title": "Kvaliteta" + }, + "settings": { + "downloadItem": "Prenesi", + "enableSubtitles": "Vklopi podnapise", + "experienceSection": "Izkušnje ogleda", + "playbackItem": "Nastavitve predvajanja", + "qualityItem": "Kvaliteta", + "sourceItem": "Viri vsebin", + "subtitleItem": "Nastavitve podnapisov", + "videoSection": "Nastavitve ogleda" + }, + "sources": { + "failed": { + "text": "Pri iskanju videoposnetkov je prišlo do napake, zato poskusite z drugim virom.", + "title": "Neuspešno iskanje" + }, + "noEmbeds": { + "text": "Nismo našli nobenih integriranih virov, poskusite z drugim virom.", + "title": "Ni bilo najdenih nobenih integriranih virov" + }, + "noStream": { + "text": "Ta vir nima tokov za ta film ali serijo.", + "title": "Ni vira" + }, + "title": "Viri", + "unknownOption": "Neznano" + }, + "subtitles": { + "customChoice": "Izberi podnapise iz datoteke", + "customizeLabel": "Prilagodi", + "offChoice": "Off", + "settings": { + "backlink": "Podnapisi po meri", + "delay": "Zamik podnapisov", + "fixCapitals": "Popravi velike začetnice" + }, + "title": "Podnapisi", + "unknownLanguage": "Neznano" + } + }, + "metadata": { + "failed": { + "badge": "Neuspešno", + "homeButton": "Domov", + "text": "Metapodatkov medija ni bilo mogoče naložiti iz baze TMDB. Preverite, ali je TMDB na vaši internetni povezavi onemogočen ali blokiran.", + "title": "Ni uspelo naložiti metapodatkov" + }, + "notFound": { + "badge": "Ni najdeno", + "homeButton": "Nazaj domov", + "text": "Medija, ki ste ga zahtevali, nismo našli. Ali je bil odstranjen ali pa ste spremenili spletni naslov.", + "title": "Tega medija ni bilo mogoče najti." + } + }, + "nextEpisode": { + "cancel": "Prekliči", + "next": "Naslednja epizoda" + }, + "playbackError": { + "badge": "Napaka v predvajanju", + "errors": { + "errorAborted": "Uporabnikova zahteva je prekinila pridobivanje medija.", + "errorDecode": "Kljub temu, da je bilo predhodno ugotovljeno, da je medijski vir uporaben, je pri poskusu dekodiranja prišlo do napake.", + "errorGenericMedia": "Piršlo je do neznane napake na mediju.", + "errorNetwork": "Prišlo je do napake v omrežju, ki je preprečila uspešno pridobivanje medija, čeprav je bil prej na voljo.", + "errorNotSupported": "Objekt medija ali ponudnik medija ni podprt." + }, + "homeButton": "Domov", + "text": "Pri poskusu predvajanja medija je prišlo do napake. Poskusite znova.", + "title": "Ni uspelo predvajati videoposnetka!" + }, + "scraping": { + "items": { + "failure": "Zgodila se je napaka", + "notFound": "Nima videoposnetka", + "pending": "Preverjanje videoposnetkov..." + }, + "notFound": { + "badge": "Ni najdeno", + "detailsButton": "Pokaži podrobnosti", + "homeButton": "Domov", + "text": "Iskali smo med našimi ponudniki ampak medija, ki ga iščete, nismo našli! Medijev ne gostimo in nimamo nadzora nad tem, katere vsebine so na voljo. Za več podrobnosti kliknite \"Prikaži podrobnosti\" spodaj.", + "title": "Iskanega nismo našli" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} do konca • Konča ob {{timeFinished, datetime}}", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + } + }, + "screens": { + "dmca": { + "text": "Dobrodošli na sudo-flixovi kontaktni strani DMCA! Spoštujemo pravice intelektualne lastnine in želimo hitro odpraviti vse težave v zvezi z avtorskimi pravicami. Če menite, da je bilo vaše avtorsko zaščiteno delo na naši platformi nepravilno uporabljeno, pošljite podrobno obvestilo DMCA na spodnji e-poštni naslov. Vključite opis avtorsko zaščitenega gradiva, svoje kontaktne podatke in izjavo o \"prepričanju v dobri veri\". Zavezani smo k takojšnji rešitvi teh zadev in cenimo vaše sodelovanje pri ohranjanju filmskega spleta, ki spoštuje ustvarjalnost in avtorske pravice.", + "title": "DMCA" + }, + "loadingApp": "Nalagam aplikacijo", + "loadingUser": "Nalagam profil", + "loadingUserError": { + "logout": "Odjava", + "reset": "Ponastavitev strežnika po meri", + "text": "Neuspešno nalaganje vašega profila", + "textWithReset": "Neuspešno nalaganje vašega profila iz strežnika po meri. Želite ponastaviti nastavitve nazaj na privzeti strežnik?" + }, + "migration": { + "failed": "Podatkov ni uspelo preseliti.", + "inProgress": "Prosimo počakajte, saj vaše podatke prenašamo. To ne bi smelo trajati dolgo." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Ime naprave", + "deviceNamePlaceholder": "Mobilni telefon", + "editProfile": "Uredi", + "logoutButton": "Odjava" + }, + "actions": { + "delete": { + "button": "Izbriši profil", + "confirmButton": "Izbriši profil", + "confirmDescription": "Ste prepričani, da želite izbrisati svoj račun? Vsi vaši podatki bodo izgubljeni!", + "confirmTitle": "Ali ste prepričani?", + "text": "Ta ukaz je nereverzibilen. Vsi podatki bodo izbrisani in nobenih podatkov ne bo mogoče obnoviti.", + "title": "Izbirši profil" + }, + "title": "Ukazi" + }, + "devices": { + "deviceNameLabel": "Ime naprave", + "failed": "Neuspešno nalaganje sej", + "removeDevice": "Odstrani", + "title": "Naprave" + }, + "profile": { + "finish": "Zaključi urejanje", + "firstColor": "Barva prvega profila", + "secondColor": "Barva drugega profila", + "title": "Urejanje slike profila", + "userIcon": "Ikona uporabnika" + }, + "register": { + "cta": "Začni", + "text": "Napredek ogleda lahko delite med napravami in jih sinhronizirate.", + "title": "Sinhroniziraj z oblakom" + }, + "title": "Profil" + }, + "appearance": { + "activeTheme": "Aktivno", + "themes": { + "blue": "Modra", + "default": "Privzeto", + "gray": "Siva", + "red": "Rdeča", + "teal": "Turkizna" + }, + "title": "Izgled" + }, + "connections": { + "server": { + "description": "Če se želite povezati z zaledjem po meri za hrambo podatkov, to omogočite in navedite URL naslov.", + "label": "Strežnik po meri", + "urlLabel": "Naslov strežnika po meri (URL)" + }, + "title": "Povezave", + "workers": { + "addButton": "Dodaj novega \"workerja\"", + "description": "Za delovanje aplikacije je ves promet usmerjen prek posredniških strežnikov. To nastavitev omogočite, če želite pripeljati lastne delavce.", + "emptyState": "Niste dodali \"workerja\", dodajte ga spodaj", + "label": "Uporaba posrednikov po meri", + "urlLabel": "Naslov Workerja (URL)", + "urlPlaceholder": "https://" + } + }, + "reset": "Ponastavi", + "save": "Shrani", + "sidebar": { + "info": { + "appVersion": "Verzija aplikacije", + "backendUrl": "Naslov zaledja", + "backendVersion": "Verzija zaledja", + "hostname": "Hostname", + "insecure": "\"Nevarna povezava\"", + "notLoggedIn": "Niste prijavljeni", + "secure": "Varna povezava", + "title": "Informacije o aplikaciji", + "unknownVersion": "Neznano", + "userId": "ID uporabnika" + } + }, + "subtitles": { + "backgroundLabel": "Motnost ozadja", + "colorLabel": "Barva", + "previewQuote": "Ne smem se bati. Strah je ubijalec misli.", + "textSizeLabel": "Velikost pisave", + "title": "Podnapisi" + }, + "unsaved": "Imate neshranjene spremembe" + } +} diff --git a/src/assets/locales/sv.json b/src/assets/locales/sv.json new file mode 100644 index 00000000..7d62aab3 --- /dev/null +++ b/src/assets/locales/sv.json @@ -0,0 +1,389 @@ +{ + "about": { + "description": "sudo-flix ä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": "sudo-flix hostar ingen innehåll. När du klickar på något att titta på, söks internet efter det valda mediet (På laddningsskärmen och i fliken 'Videokällor' kan du se vilken källa du använder). Inget medium laddas upp av sudo-flix, allt sker genom sökmotorn.", + "title": "Var kommer innehållet ifrån?" + }, + "q2": { + "body": "Det går inte att begära en show eller film, sudo-flix 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 sudo-flix" + }, + "actions": { + "copied": "Kopierad", + "copy": "Kopiera" + }, + "auth": { + "createAccount": "Har du inget konto ännu? <0>Skapa ett konto.", + "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.", + "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}} - 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": "Friskriver sig från ansvar", + "disclaimerText": "sudo-flix hostar inga filer utan länkar endast till filer från tredje part. Eventuella juridiska frågor bör tas upp med källorna till materialet. sudo-flix varken ansvarar eller styr över de mediefiler som visas." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Titta på dina favoritprogram och filmer helt gratis." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Om oss", + "dmca": "DMCA", + "login": "Logga in", + "pagetitle": "{{title}} - sudo-flix", + "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": { + "downloads": { + "disclaimer": "Nedladdningar görs direkt från leverantören. sudo-flix har ingen kontroll över hur nedladdningarna tillhandahålls.", + "downloadVideo": "Ladda ner video", + "onAndroid": { + "1": "För att ladda ner på Android, klicka på nedladdningsknappen och på den nya sidan trycker och håller på videon, välj sedan spara.", + "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å , sedan Spara i filer .", + "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 Spara video som", + "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 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 för att få olika kvalitetsoptioner.", + "title": "Kvalitet" + }, + "settings": { + "downloadItem": "Ladda ner", + "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 sudo-flixs 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 sudo-flix 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" + }, + "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://" + } + }, + "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" + } +} diff --git a/src/assets/locales/ta.json b/src/assets/locales/ta.json new file mode 100644 index 00000000..0b827ceb --- /dev/null +++ b/src/assets/locales/ta.json @@ -0,0 +1,67 @@ +{ + "about": { + "faqTitle": "பொதுவான கேள்விகள்", + "title": "sudo-flix பற்றி" + }, + "actions": { + "copied": "நகலெடுக்கப்பட்டது", + "copy": "நகல்" + }, + "auth": { + "createAccount": "கணக்கு இல்லையா? <0>கணக்கை உருவாக்குங்கள்", + "deviceNameLabel": "கருவியின் பெயர்", + "deviceNamePlaceholder": "எனது கைபேசி", + "generate": { + "description": "தங்கள் கடவுச்சொற்றொடரே தங்களது பயனர் பெயர் மற்றும் கடவுச்சொல். கணக்கினுள் நுழைய அதனை பாதுகாப்பாக வைத்திருங்கள்", + "next": "கடவுச்சொற்றொடரை சேமித்துவிட்டேன்", + "passphraseFrameLabel": "கடவுச்சொற்றொடர்", + "title": "உங்கள் கடவுச்சொற்றொடர்" + }, + "hasAccount": "கணக்கு வைத்துள்ளீரா? <0>புகுபதிகை செய்க ", + "login": { + "description": "உங்கள் கணக்கினுள் புகுபதிய கடவுச்சொற்றொடரை உள்ளிடுக", + "deviceLengthError": "கருவியின் பெயரை உள்ளிடுக", + "passphraseLabel": "12-சொல் கடவுச்சொற்றொடர்", + "passphrasePlaceholder": "கடவுச்சொற்றொடர்", + "submit": "புகுபதிகை", + "title": "உங்கள் கணக்கினுள் புகுபதிய", + "validationError": "தவறான அல்லது முழுமையற்ற கடவுச்சொற்றொடர்" + }, + "register": { + "information": { + "icon": "பயனர் குறிப்படம்", + "next": "அடுத்து", + "title": "கணக்கு விவரம்" + } + }, + "trust": { + "no": "பின்செல்" + }, + "verify": { + "register": "கணக்கை உருவாக்கு", + "title": "கடவுச்சொற்றொடரை உறுதி செய்க" + } + }, + "global": { + "pages": { + "login": "புகுபதிகை", + "settings": "அமைப்புகள்" + } + }, + "home": { + "search": { + "sectionTitle": "தேடல் முடிவுகள்" + } + }, + "media": { + "types": { + "movie": "திரைப்படம்", + "show": "காட்சி" + } + }, + "navigation": { + "menu": { + "settings": "அமைப்புகள்" + } + } +} diff --git a/src/assets/locales/th.json b/src/assets/locales/th.json new file mode 100644 index 00000000..7c26c6d5 --- /dev/null +++ b/src/assets/locales/th.json @@ -0,0 +1,413 @@ +{ + "about": { + "description": "sudo-flix เป็นเว็บแอปพลิเคชันที่ค้นหาสตรีมทางอินเทอร์เน็ต ทีมงานตั้งเป้าไปที่แนวทางการบริโภคเนื้อหาแบบมินิมอลเป็นส่วนใหญ่", + "faqTitle": "คําถามทั่วไป", + "q1": { + "body": "sudo-flix ไม่ได้เป็นเจ้าของเนื้อหาใด ๆ เมื่อคุณคลิกที่บางสิ่งเพื่อดู อินเทอร์เน็ตจะถูกค้นหาสื่อที่เลือก (บนหน้าจอโหลดและในแท็บ 'แหล่งวิดีโอ' คุณจะเห็นว่าคุณกําลังใช้แหล่งใด) สื่อไม่เคยถูกอัปโหลดโดย sudo-flix ทุกอย่างผ่านกลไกการค้นหานี้", + "title": "เนื้อหามาจากที่ไหน?" + }, + "q2": { + "body": "ไม่สามารถขอซีรี่ย์หรือภาพยนตร์ได้ sudo-flix ไม่ได้จัดการเนื้อหาใด ๆ เนื้อหาทั้งหมดถูกดูผ่านแหล่งข้อมูลบนอินเทอร์เน็ต", + "title": "ฉันจะขอซีรี่ย์หรือภาพยนตร์ได้ที่ไหน?" + }, + "q3": { + "body": "ผลการค้นหาของเราขับเคลื่อนโดย The Movie Database (TMDB) และจะแสดงไม่ว่าแหล่งข้อมูลของเราจะมีเนื้อหาจริงหรือไม่", + "title": "ผลการค้นหาซีรี่ย์หรือภาพยนตร์ ทําไมฉันถึงเล่นไม่ได้?" + }, + "title": "เกี่ยวกับ sudo-flix" + }, + "actions": { + "copied": "คัดลอกแล้ว", + "copy": "คัดลอก" + }, + "auth": { + "createAccount": "ยังไม่มีบัญชีใช่หรือไม่?<0>สร้างบัญชี", + "deviceNameLabel": "ชื่ออุปกรณ์", + "deviceNamePlaceholder": "โทรศัพท์ส่วนบุคคล", + "generate": { + "description": "รหัสผ่านของคุณถูกตั้งเช่นเดียวกับชื่อผู้ใช้และรหัสผ่าน โปรดตรวจสอบให้แน่ใจว่ารหัสผ่านของคุณถูกเก็บอย่างปลอดภัย คุณจำเป็นต้องใช้เพื่อเข้าสู่ระบบบัญชีของคุณ", + "next": "ฉันบันทึกรหัสผ่านของฉันแล้ว", + "passphraseFrameLabel": "หรัสผ่าน", + "title": "หรัสผ่านของคุณ" + }, + "hasAccount": "คุณมีบัญชีแล้วหรือไม่? <0>เข้าสู่ระบบที่นี่.", + "login": { + "description": "โปรดป้อนสหัสผ่านของคุณเพื่อเข้าสู่ระบบ", + "deviceLengthError": "โปรดป้อนชื่ออุปกรณ์ของคุณ", + "passphraseLabel": "หรัสผ่าน 12 ตัว", + "passphrasePlaceholder": "หรัสผ่าน", + "submit": "เข้าสู่ระบบ", + "title": "เข้าสู่ระบบบัญชีของคุณที่นี่", + "validationError": "รหัสผ่านไม่ถูกต้อง หรือ รหัสผ่านไม่สมบูรณ์" + }, + "register": { + "information": { + "color1": "สีโปรไฟล์ 1", + "color2": "สีโปรไฟล์ 2", + "header": "โปรดใส่ชื่ออุปกรณ์ของคุณและเลือกสีจากนั้นเลือกสัญลักษณ์ผู้ใช้ที่คุณต้องการ", + "icon": "สัญลักษณ์ผู้ใช้", + "next": "ต่อไป", + "title": "ข้อมูลบัญชี" + } + }, + "trust": { + "failed": { + "text": "คุณทำตามขั้นตอนอย่างถูกต้องหรือไม่?", + "title": "การเข้าสู่ระบบล้มเหลว" + }, + "host": "คุณกำลังเชื่อมต่อกับ <0>{{hostname}} - โปรดกดตกลงเชื่อถือเครือข่ายนี้ก่อนสร้างบัญชี", + "no": "ย้อนกลับ", + "title": "คุณเชื่อเครือข่ายนี้หรือไม่?", + "yes": "ฉันเชื่อเครือข่ายนี้" + }, + "verify": { + "description": "โปรดป้อนรหัสผ่านก่อนหน้านี้ของคุณ เพื่อยืนยันว่าคุณได้บันทึกและเพื่อสร้างบัญชีของคุณ", + "invalidData": "ข้อไม่ถูกต้อง", + "noMatch": "รหัสผ่านไม่ตรงกัน", + "passphraseLabel": "รหัสผ่าน 12 ตัว ของคุณ", + "recaptchaFailed": "การตรวจสอบล้มเหลว", + "register": "สร้างบัญชี", + "title": "ยืนยันรหัสผ่านของคุณ" + } + }, + "errors": { + "badge": "ไม่สามารถใช้งานได้", + "details": "ข้อมูลผิดพลาด", + "reloadPage": "โหลดหน้าใหม่", + "showError": "แสดงข้อมูลที่ผิดพลาด", + "title": "เราพบข้อผิดพลาด!" + }, + "footer": { + "legal": { + "disclaimer": "ข้อจํากัดความรับผิดชอบ", + "disclaimerText": "sudo-flix ไม่ได้เป็นเจ้าของไฟล์ใด ๆ เป็นเพียงลิงก์ไปยังบริการของบุคคลที่สาม ประเด็นทางกฎหมายควรดําเนินการกับเจ้าของไฟล์ และผู้ให้บริการ sudo-flix ไม่รับผิดชอบต่อไฟล์สื่อใด ๆ ที่แสดงโดยผู้ให้บริการวิดีโอ" + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "ดูรายการซีรี่ย์และภาพยนตร์ที่คุณชื่นชอบด้วยแอปสตรีมนี้" + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "เกี่ยวกับ", + "dmca": "DMCA", + "login": "เข้าสู่ระบบ", + "pagetitle": "{{title}} - sudo-flix", + "register": "ลงทะเบียน", + "settings": "ตั้งค่า" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "รายการของฉัน" + }, + "continueWatching": { + "sectionTitle": "ดูต่อ" + }, + "mediaList": { + "stopEditing": "หยุดแก้ไข" + }, + "search": { + "allResults": "นั่นคือทั้งหมดที่เรามี!", + "failed": "ไม่พบสื่อนี้ ลองอีกครั้ง!", + "loading": "กำลังโหลด..", + "noResults": "เราไม่พบอะไรเลย!", + "placeholder": "คุณอยากดูอะไรคะ?", + "sectionTitle": "ผลการค้นหา" + }, + "titles": { + "day": { + "default": "คุณอยากดูเรื่องอะไรในช่วงบ่ายนี้?" + }, + "morning": { + "default": "คุณอยากดูอะไรเช้านี้?", + "extra": ["ฉันได้ยินมาว่าเรื่อง Before Sunrise สนุก"] + }, + "night": { + "default": "คุณอยากดูเรื่องอะไรในช่วงค่ำ?", + "extra": ["เหนื่อยมั้ย? ฉันได้ยินมาว่า The Exorcist นั้นดี"] + } + } + }, + "media": { + "episodeDisplay": "ภ{{season}} ต{{episode}}", + "types": { + "movie": "ภาพยนตร์", + "show": "ซีรี่ย์" + } + }, + "navigation": { + "banner": { + "offline": "ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ" + }, + "menu": { + "about": "เกี่ยวกับเรา", + "donation": "บริจาค", + "logout": "ออกจากระบบ", + "register": "เชื่อมต่อกับคลาวด์", + "settings": "ตั้งค่า", + "support": "ช่วยเหลือ" + } + }, + "notFound": { + "badge": "ไม่พบ", + "goHome": "กลับไปที่หน้าหลัก", + "message": "เรามองหาทั่วทุกที่แล้ว : ใต้ถังขยะ ในตู้เสื้อผ้า และข้างหลังพร็อกซิ แต่เราไม่พบหน้าที่คุณตามหา", + "title": "ไม่พบหน้านั้น" + }, + "overlays": { + "close": "ปิด" + }, + "player": { + "back": { + "default": "กลับไปที่หน้าหลัก", + "short": "กลับ" + }, + "casting": { + "enabled": "เชื่อมต่ออุปกรณ์.." + }, + "menus": { + "downloads": { + "disclaimer": "การดาวน์โหลดจะถูกนํามาจากผู้ให้บริการโดยตรง sudo-flix ไม่สามารถควบคุมวิธีการดาวน์โหลดได้", + "downloadPlaylist": "ดาวน์โหลดเพลย์ลิสต์", + "downloadSubtitle": "ดาวน์โหลดคำบรรยายนี้", + "downloadVideo": "ดาวน์โหลดวิดีโอ", + "hlsDisclaimer": "การดาวน์โหลดจะเป็นการดาวน์โหลดโดยตรงจากผู้ให้บริการ โดยเว็บไซต์ sudo-flix ไม่สามารถควบคุมวิธีการที่ไฟล์จะถูกดาวน์โหลด โปรดทราบว่าหากคุณกำลังดาวน์โหลดเพลย์ลิสต์ HLS ซึ่งมีจุดประสงค์สำหรับผู้ใช้ที่มีความเข้าใจใน สตรีมมิ่ง และมัลติมีเดียขั้นสูง.", + "onAndroid": { + "1": "หากต้องการดาวน์โหลดบน Android ให้คลิกปุ่มดาวน์โหลด จากนั้นในหน้าใหม่ แตะ บนวิดีโอค้างไว้ จากนั้นเลือก บันทึก", + "shortTitle": "ดาวน์โหลด / Android", + "title": "กําลังดาวน์โหลดบน Android" + }, + "onIos": { + "1": "หากต้องการดาวน์โหลดบน iOS ให้คลิกปุ่มดาวน์โหลด จากนั้นในหน้าใหม่ ให้คลิก จากนั้น บันทึกลงในไฟล์ ", + "shortTitle": "ดาวน์โหลด / iOS", + "title": "กําลังดาวน์โหลดบน iOS" + }, + "onPc": { + "1": "คลิกปุ่มดาวน์โหลดบนคอมพิวเตอร์ จากนั้นในหน้าใหม่ คลิกขวาที่วิดีโอแล้วเลือก บันทึกวิดีโอเป็น", + "shortTitle": "ดาวน์โหลด / คอมพิวเตอร์", + "title": "กําลังดาวน์โหลดบนคอมพิวเตอร์" + }, + "title": "ดาวน์โหลด" + }, + "episodes": { + "button": "ตอน", + "emptyState": "ไม่มีตอนเหล่านี้ในภาคนี้ โปรดกลับมาดูทีหลัง!", + "episodeBadge": "ตอนที่ {{episode}}", + "loadingError": "การโหลดภาคผิดพลาด", + "loadingList": "กำลังโหลด…", + "loadingTitle": "กำลังโหลด…" + }, + "playback": { + "speedLabel": "ความเร็วในการเล่น", + "title": "การตั้งค่าการเล่น" + }, + "quality": { + "automaticLabel": "คุณภาพอัตโนมัติ", + "hint": "คุณสามารถลอง <0>สลับแหล่งที่มา เพื่อรับตัวเลือกคุณภาพที่แตกต่างกัน", + "iosNoQuality": "เนื่องจากข้อจํากัดที่กําหนดโดย Apple การเลือกคุณภาพจึงไม่พร้อมใช้งานบน iOS สําหรับแหล่งข้อมูลนี้ คุณสามารถลอง <0>สลับไปยังแหล่งอื่น เพื่อรับตัวเลือกคุณภาพที่แตกต่างกัน", + "title": "คุณภาพ" + }, + "settings": { + "downloadItem": "ดาวน์โหลด", + "enableSubtitles": "เปิดคำบรรยาย", + "experienceSection": "ประสบการณ์รับชม", + "playbackItem": "ตั้งค่าการเล่น", + "qualityItem": "คุณภาพ", + "sourceItem": "แหล่งที่มาของวิดีโอ", + "subtitleItem": "ตั้งค่าคำบรรยาย", + "videoSection": "ตั้งค่าวิดีโอ" + }, + "sources": { + "failed": { + "text": "มีข้อผิดพลาดขณะพยายามค้นหาวิดีโอ โปรดลองใช้แหล่งอื่น", + "title": "ค้นหาไม่พบ" + }, + "noEmbeds": { + "text": "เราไม่พบการฝังใด ๆ โปรดลองใช้แหล่งอื่น", + "title": "ไม่พบการฝัง" + }, + "noStream": { + "text": "แหล่งที่มานี้ไม่มีสตรีมสำหรับภาพยนตร์หรือซีรี่ย์เรื่องนี้", + "title": "ไม่มีสตรีม" + }, + "title": "แหล่งที่มา", + "unknownOption": "ไม่ทราบ" + }, + "subtitles": { + "customChoice": "เลือกคำบรรยายจากไฟล์", + "customizeLabel": "ปรับแต่ง", + "offChoice": "ปิด", + "settings": { + "backlink": "คำบรรยายแบบกำหนดเอง", + "delay": "เวลาดีเลย์คำบรรยาย" + }, + "title": "คำบรรยาย", + "unknownLanguage": "ไม่ทราบ" + } + }, + "metadata": { + "failed": { + "badge": "ล้มเหลว", + "homeButton": "กลับหน้าหลัก", + "text": "ไม่สามารถโหลดข้อมูล meta ของสื่อจาก TMDB ได้ โปรดตรวจสอบว่า TMDB ล่มหรือถูกบล็อกในการเชื่อมต่ออินเทอร์เน็ตของคุณ", + "title": "โหลดข้อมูล meta ไม่สำเร็จ" + }, + "notFound": { + "badge": "ไม่พบ", + "homeButton": "กลับไปที่หน้าหลัก", + "text": "เราไม่พบสื่อที่คุณร้องขอ ไม่ว่าจะถูกลบออก หรือคุณดัดแปลงกับ URL", + "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 ของ sudo-flix! เราเคารพสิทธิ์ในทรัพย์สินทางปัญญาและต้องการแก้ไขข้อกังวลด้านลิขสิทธิ์อย่างรวดเร็ว หากคุณเชื่อว่างานที่มีลิขสิทธิ์ของคุณถูกใช้อย่างไม่เหมาะสมบนแพลตฟอร์มของเรา โปรดส่งประกาศ DMCA โดยละเอียดไปยังอีเมลด้านล่าง โปรดระบุคําอธิบายของเนื้อหาที่มีลิขสิทธิ์ รายละเอียดการติดต่อของคุณ และคําแถลงความเชื่อด้วยความสุจริต เรามุ่งมั่นที่จะแก้ไขเรื่องเหล่านี้อย่างทันท่วงทีและขอขอบคุณสําหรับความร่วมมือของคุณในการทําให้ sudo-flix เป็นสถานที่ที่เคารพความคิดสร้างสรรค์และลิขสิทธิ์", + "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": "สีของโปรไฟล์ 1", + "secondColor": "สีของโปรไฟล์ 2", + "title": "แก้ไขรูปโปรไฟล์", + "userIcon": "สัญลักษณ์ผู้ใช้" + }, + "register": { + "cta": "เริ่มเลย", + "text": "แบ่งปันความคืบหน้าการรับชมระหว่างอุปกรณ์และเชื่อมต่อเข้าด้วยกัน", + "title": "เชื่อมต่อกับคลาวด์" + }, + "title": "บัญชี" + }, + "appearance": { + "activeTheme": "ใช้งาน", + "themes": { + "blue": "ฟ้า", + "default": "ค่าเริ่มต้น", + "gray": "เทา", + "red": "แดง", + "teal": "เขียวแกมน้ำเงิน" + }, + "title": "ธีม" + }, + "connections": { + "server": { + "description": "หากคุณต้องการเชื่อมต่อกับ backend ที่กําหนดเองเพื่อจัดเก็บข้อมูลของคุณ ให้เปิดใช้งานสิ่งนี้และระบุ URL", + "label": "กำหนดเซิร์ฟเวอร์เอง", + "urlLabel": "กําหนด URL เซิร์ฟเวอร์เอง" + }, + "title": "การเชื่อมต่อ", + "workers": { + "addButton": "เพิ่มผู้ช่วยใหม่", + "description": "เพื่อให้แอปพลิเคชันทํางาน การรับส่งข้อมูลทั้งหมดจะถูกส่งผ่านพร็อกซี่ เปิดใช้งานสิ่งนี้หากคุณต้องการนำเข้างานของคุณเอง", + "emptyState": "ยังไม่มีลูกทีม เพิ่มหนึ่งคนด้านล่าง", + "label": "ใช้พร็อกซี่แบบกําหนดเอง", + "urlLabel": "ลูกทีม URLs", + "urlPlaceholder": "https://" + } + }, + "reset": "เริ่มใหม่", + "save": "บันทึก", + "sidebar": { + "info": { + "appVersion": "เวอร์ชันแอป", + "backendUrl": "ระบบจัดการเว็บไซต์ URL", + "backendVersion": "เวอร์ชันหลัก", + "hostname": "ชื่อผู้ใช้", + "insecure": "ไม่ปลอดภัย", + "notLoggedIn": "คุณไม่ได้เข้าสู่ระบบ", + "secure": "ความปลอดภัย", + "title": "ข้อมูลแอปพลิเคชัน", + "unknownVersion": "ไม่ทราบ", + "userId": "รหัสผู้ใช้" + } + }, + "subtitles": { + "backgroundLabel": "ความทึบของพื้นหลัง", + "colorLabel": "สี", + "previewQuote": "คำบรรยายจะมีลักษณะแบบนี้", + "textSizeLabel": "ขนาดข้อความ", + "title": "คำบรรยาย" + }, + "unsaved": "คุณได้บันทึกการเปลี่ยนแปลงแล้ว" + } +} diff --git a/src/assets/locales/tok.json b/src/assets/locales/tok.json new file mode 100644 index 00000000..fee83174 --- /dev/null +++ b/src/assets/locales/tok.json @@ -0,0 +1,390 @@ +{ + "about": { + "description": "ilo Muwi-We li lukin li wile e sitelen. kulupu lawa li lawa pona", + "faqTitle": "sona wile", + "q1": { + "body": "ilo Muwi-We li mama ala e sitelen. sina lukin e sitelen la mi alasa e sitelen lon lipu ale. (sina ken sona e mama sitelen lon tenpo alasa lon poki 'mama'.) sitelen li awen ala lon ilo Muwi-We. ale li lon alasa ni", + "title": "sitelen li tan seme?" + }, + "q2": { + "body": "sina ken ala wile alasa e sitelen. ilo Muwi-We li lawa ala e sitelen. sitelen li tan lipu mama ante", + "title": "mi wile alasa e sitelen lon seme?" + }, + "q3": { + "body": "sitelen lon lukin ilo li tan lipu The Movie Database (lipu TMDB). jo li ante ala e ni", + "title": "sitelen pi wile mi li lon lukin ilo. seme la mi ken ala lukin e ona?" + }, + "title": "ilo Muwi-We la" + }, + "actions": { + "copied": "sama a", + "copy": "o sama" + }, + "auth": { + "createAccount": "sina jo ala e lipu open la <0>o pali e lipu open", + "deviceNameLabel": "nimi ilo", + "deviceNamePlaceholder": "ilo mi", + "generate": { + "description": "nimi open sina li lon nasin open pi lipu open sina. o awen pona e ona. sina open e lipu open sina la sina wile pana e nimi open ale sina", + "next": "mi jo e nimi open mi", + "passphraseFrameLabel": "nimi open", + "title": "nimi open sina" + }, + "hasAccount": "sina jo e lipu open la <0>o pana e nimi sina", + "login": { + "description": "lipu open sina la o sitelen e nimi ale sina", + "deviceLengthError": "o pana e nimi ilo", + "passphraseLabel": "nimi open 12", + "passphrasePlaceholder": "nimi open", + "submit": "o open", + "title": "o pana e nimi sina", + "validationError": "pakala" + }, + "register": { + "information": { + "color1": "kule nanpa wan", + "color2": "kule nanpa tu", + "header": "o pana e nimi pi ilo sina e kule e sitelen", + "icon": "sitelen", + "next": "o pana", + "title": "lipu open" + } + }, + "trust": { + "failed": { + "text": "nasin ale sina li pona anu seme?", + "title": "toki tawa lawa ilo li pakala" + }, + "host": "lawa ilo sina li <0>{{hostname}} - ona li pona tawa sina la sina ken pali e lipu open", + "no": "o weka", + "title": "lawa ilo ni li pona tawa sina anu seme?", + "yes": "lawa ilo ni li pona" + }, + "verify": { + "description": "sina awen e nimi open sina la o pana e nimi open", + "invalidData": "pakala", + "noMatch": "nimi li sama ala", + "passphraseLabel": "nimi open 12 sina", + "recaptchaFailed": "nasin ReCaptcha li pakala", + "register": "o pali e lipu open", + "title": "nimi open sina" + } + }, + "errors": { + "badge": "pakala a", + "details": "pakala", + "reloadPage": "o sin e lipu", + "showError": "o pana e sona pakala", + "title": "mi kama pakala a!" + }, + "footer": { + "legal": { + "disclaimer": "o sona e ni:" + }, + "links": { + "discord": "kulupu Siko", + "dmca": "DMCA", + "github": "lipu Kita" + }, + "tagline": "o lukin e sitelen ale pi wile sina kepeken ilo ni" + }, + "global": { + "name": "ilo Muwi-We", + "pages": { + "about": "o sona", + "dmca": "DMCA", + "login": "o open", + "pagetitle": "{{title}} - ilo Muwi-We", + "register": "o pali open", + "settings": "o ante" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "poki awen" + }, + "continueWatching": { + "sectionTitle": "o awen lukin" + }, + "mediaList": { + "stopEditing": "o pini ante" + }, + "search": { + "allResults": "pini a!", + "failed": "lukin li pakala a! o alasa sin", + "loading": "alasa...", + "noResults": "ijo li lon ala a!", + "placeholder": "sina wile lukin e seme?", + "sectionTitle": "mi lukin e ni:" + }, + "titles": { + "day": { + "default": "tenpo suno ni la sina wile lukin e seme?", + "extra": ["sina pilin alasa la o lukin e sitelen Jurassic Park"] + }, + "morning": { + "default": "tenpo sin ni la sina wile lukin e seme?", + "extra": ["ken la sitelen Before Sunrise li pona"] + }, + "night": { + "default": "tenpo pimeja ni la sina wile lukin e seme?", + "extra": ["sina pilin lape anu seme? o alasa lukin e sitelen Exorcist"] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} E{{episode}}", + "types": { + "movie": "sitelen wan", + "show": "sitelen kulupu" + } + }, + "navigation": { + "banner": { + "offline": "wawa linja sina li seme?" + }, + "menu": { + "about": "mi", + "donation": "o esun", + "logout": "o weka sona e sina", + "register": "Sync to cloud", + "settings": "o ante", + "support": "o supa" + } + }, + "notFound": { + "badge": "lon ala", + "goHome": "o tawa weka", + "message": "mi lukin lon anpa poki, lon insa tomo, lon monsi ilo. taso mi lukin ala e lipu pi wile sina", + "title": "mi ken ala lukin e lipu ona" + }, + "overlays": { + "close": "o pini" + }, + "player": { + "back": { + "default": "o tawa weka", + "short": "o tawa" + }, + "casting": { + "enabled": "mi pana e sitelen tawa ilo ante..." + }, + "menus": { + "downloads": { + "disclaimer": "sina kama jo e sitelen tawa ilo sina tan lipu mama. ilo Muwi-We li lawa ala e nasin jo", + "downloadPlaylist": "o jo e lipu sitelen", + "downloadSubtitle": "o jo e nimi toki", + "downloadVideo": "o jo e sitelen", + "hlsDisclaimer": "jo li tan mama. ilo Muwi-We li lawa ala e nasin jo. o sona e ni: sina jo e lipu sitelen HLS. ni li tawa jan pi sona sitelen", + "onAndroid": { + "1": "ilo Android la o luka e nena 'o jo'. lon lipu sin la o luka wawa e sitelen. o luka e nena 'o jo'", + "shortTitle": "Android ) o jo", + "title": "o jo lon ilo Android" + }, + "onIos": { + "1": "ilo iOS la o luka e nena 'o jo'. lon lipu sin la o luka e , e o jo poki .", + "shortTitle": "iOS ) o jo", + "title": "o jo lon ilo iOS" + }, + "onPc": { + "1": "ilo suli la o luka e nena 'o jo e sitelen'. lon lipu sin la o luka ante e sitelen. o luka e nena 'o jo e sitelen lon nimi ante'", + "shortTitle": "ilo suli ) o jo", + "title": "o jo lon ilo suli" + }, + "title": "kama jo" + }, + "episodes": { + "button": "sitelen mute", + "emptyState": "kulupu ni li jo ala e sitelen. o lukin lon tenpo ante a!", + "episodeBadge": "E{{episode}}", + "loadingError": "alasa li pakala", + "loadingList": "alasa...", + "loadingTitle": "alasa..." + }, + "playback": { + "speedLabel": "tenpo lukin", + "title": "nasin pali" + }, + "quality": { + "automaticLabel": "ilo o lawa", + "hint": "ante pi pona lukin la sina ken <0>ante e mama", + "iosNoQuality": "tan lawa pi esun Apple la ante pi pona lukin li ken ala lon ilo iOS. ante pi pona lukin la o alasa <0>ante e mama", + "title": "pona lukin" + }, + "settings": { + "downloadItem": "o jo", + "enableSubtitles": "nimi toki", + "experienceSection": "nasin lukin", + "playbackItem": "nasin pali", + "qualityItem": "pona lukin", + "sourceItem": "mama sitelen", + "subtitleItem": "nimi toki", + "videoSection": "nasin sitelen" + }, + "sources": { + "failed": { + "text": "lukin sitelen li pakala. o alasa e mama ante.", + "title": "pakala jo" + }, + "noEmbeds": { + "text": "mi lukin ala e lipu. o alasa e mama ante.", + "title": "lipu li lon ala" + }, + "noStream": { + "text": "lipu mama ni li jo ala e sitelen ni", + "title": "sitelen li lon ala" + }, + "title": "mama", + "unknownOption": "mi sona ala" + }, + "subtitles": { + "customChoice": "o pana tan ilo", + "customizeLabel": "o ante", + "offChoice": "ala", + "settings": { + "backlink": "nimi toki ante", + "delay": "o ante e tenpo nimi", + "fixCapitals": "o pona e suli nimi" + }, + "title": "nimi toki", + "unknownLanguage": "mi sona ala" + } + }, + "metadata": { + "failed": { + "badge": "pakala", + "homeButton": "o tawa weka", + "text": "mi pakala alasa e sona sitelen tan lipu TMDB. ken la ona li lape anu moli", + "title": "mi pakala alasa e sona sitelen" + }, + "notFound": { + "badge": "lon ala", + "homeButton": "o tawa weka", + "text": "mi lukin ala e lipu pi wile sina. lipu li weka anu ante", + "title": "mi lukin ala e ona" + } + }, + "nextEpisode": { + "cancel": "o ala", + "next": "o sitelen sin" + }, + "playbackError": { + "badge": "pakala lukin", + "errors": { + "errorAborted": "jan li pini alasa e sitelen", + "errorDecode": "sitelen li kama ante la pakala li kama lon", + "errorGenericMedia": "pakala sitelen pi sona ala li lon", + "errorNetwork": "pakala linja li pakala e alasa sitelen", + "errorNotSupported": "mi ken ala supa e sitelen anu mama sitelen" + }, + "homeButton": "o tawa weka", + "text": "mi alasa lukin e sitelen. taso pakala li lon. o alasa sin", + "title": "mi pakala lukin a" + }, + "scraping": { + "items": { + "failure": "mi pakala", + "notFound": "sitelen li lon ala", + "pending": "mi alasa e sitelen..." + }, + "notFound": { + "badge": "lon ala", + "detailsButton": "o pana e sona", + "homeButton": "o tawa weka", + "text": "mi lukin e lipu mama ale mi. taso mi lukin ala e ijo pi wile sina. mi mama ala e sitelen. mi lawa ala e sitelen. o luka e nena anpa", + "title": "mi lukin ala e ona" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "tenpo {{timeLeft}} la o awen • tenpo {{timeFinished, datetime}} la ona li pini", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + } + }, + "screens": { + "dmca": { + "title": "DMCA" + }, + "loadingApp": "mi alasa e ilo", + "loadingUser": "mi alasa e lipu sina", + "loadingUserError": { + "logout": "o weka sona e sina", + "reset": "o sin e lawa ilo sina", + "text": "alasa li pakala", + "textWithReset": "alasa tan lawa ilo sina li pakala. sina wile e lawa ilo mi anu seme?" + }, + "migration": { + "inProgress": "o awen lili. mi alasa tawa e sona sina" + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "nimi ilo", + "deviceNamePlaceholder": "ilo", + "editProfile": "o ante", + "logoutButton": "o weka sona e ona" + }, + "actions": { + "delete": { + "button": "o weka e ale", + "confirmButton": "o weka e ale", + "confirmDescription": "weka ni li suli li wawa mute a! sona ale sina li ken ala kama lon tenpo ante a!", + "confirmTitle": "sina wile e ni anu seme?", + "text": "tenpo kama la sina ken ala ante e weka ni. ale li kama weka", + "title": "o weka e ale" + }, + "title": "pali" + }, + "devices": { + "deviceNameLabel": "nimi ilo", + "failed": "alasa li pakala", + "removeDevice": "o weka", + "title": "ilo" + }, + "profile": { + "finish": "o ante", + "firstColor": "kule nanpa wan", + "secondColor": "kule nanpa tu", + "title": "o ante e sitelen", + "userIcon": "sitelen" + }, + "register": { + "cta": "o open", + "text": "o sama e lipu open sina tawa ilo sina ante", + "title": "o sama e lipu" + }, + "title": "lipu open" + }, + "appearance": { + "activeTheme": "lon", + "themes": { + "blue": "laso", + "default": "sama", + "gray": "pimeja", + "red": "loje", + "teal": "laso walo" + }, + "title": "kule" + }, + "connections": { + "server": { + "description": "sina wile e poki sona ante la o pana e nimi ona lon ni", + "label": "lawa ante", + "urlLabel": "nimi pi lawa ante" + }, + "title": "kulupu" + }, + "reset": "o weka e ante", + "save": "o ante", + "subtitles": { + "backgroundLabel": "kon", + "colorLabel": "kule", + "previewQuote": "kijetesantakalu tonsi li lanpan ala lanpan e soko?", + "textSizeLabel": "suli", + "title": "nimi toki" + }, + "unsaved": "ante sina li awen ala" + } +} diff --git a/src/assets/locales/tr.json b/src/assets/locales/tr.json new file mode 100644 index 00000000..784f8540 --- /dev/null +++ b/src/assets/locales/tr.json @@ -0,0 +1,428 @@ +{ + "about": { + "description": "sudo-flix internette akışlar için tarama yapan bir web uygulamasıdır. Ekip, içerik tüketmeye karşı ekseriyetle minimalist bir yaklaşım sergilemektedir.", + "faqTitle": "Sıkça sorulan sorular", + "q1": { + "body": "sudo-flix herhangi bir içerik barındırmaz. İzlemek için bir şeye tıkladığınızda, seçilen medya için internette arama yapılır (Yükleme ekranında ve 'video kaynakları' sekmesinde hangi kaynağı kullandığınızı görebilirsiniz). Medya hiçbir zaman sudo-flix tarafından yüklenmez, her şey bu arama mekanizması aracılığıyla gerçekleşir.", + "title": "İçerikler nereden geliyor?" + }, + "q2": { + "body": "Bir dizi veya film talep etmek mümkün değildir, sudo-flix içeriklerin hiçbirini yönetmez. Tüm içerikler internet üzerindeki kaynaklar aracılığıyla görüntülenir.", + "title": "Bir dizi veya filmi nereden talep edebilirim?" + }, + "q3": { + "body": "Arama sonuçlarımız The Movie Database (TMDB) tarafından desteklenmektedir ve kaynaklarımızın içeriğe gerçekten sahip olup olmadığına bakılmaksızın görüntülenir.", + "title": "Arama sonuçları diziyi veya filmi gösteriyor, neden oynatamıyorum?" + }, + "title": "sudo-flix hakkında" + }, + "actions": { + "copied": "Kopyalandı", + "copy": "Kopyala" + }, + "auth": { + "createAccount": "Henüz bir hesabınız yok mu? <0>Hesap oluşturun.", + "deviceNameLabel": "Cihaz ismi", + "deviceNamePlaceholder": "Kişisel telefon", + "generate": { + "description": "Şifreleme anahtarınız, kullanıcı adınız ve parolanız olarak işlev görür. Hesabınıza giriş yapmak için bu parolayı girmeniz gerekeceğinden onu güvende tuttuğunuzdan emin olun", + "next": "Şifreleme anahtarımı kaydettim", + "passphraseFrameLabel": "Şifreleme anahtarı", + "title": "Şifreleme anahtarınız" + }, + "hasAccount": "Zaten hesabınız var mı?<0>Giriş yapın.", + "login": { + "description": "Hesabınıza giriş yapmak için lütfen şifreleme anahtarınızı girin", + "deviceLengthError": "Lütfen bir cihaz ismi girin", + "passphraseLabel": "12 kelimelik şifreleme anahtarı", + "passphrasePlaceholder": "Şifreleme anahtarı", + "submit": "Giriş yap", + "title": "Hesabınıza giriş yapın", + "validationError": "Yanlış veya eksik şifreleme anahtarı" + }, + "register": { + "information": { + "color1": "Birinci hesap rengi", + "color2": "İkinci hesap rengi", + "header": "Cihazınız için bir isim girin, zevkinize göre renk ve kullanıcı simgesi seçin", + "icon": "Kullanıcı simgesi", + "next": "İleri", + "title": "Hesap bilgisi" + } + }, + "trust": { + "failed": { + "text": "Doğru şekilde yapılandırdınız mı?", + "title": "Sunucuya ulaşılamadı" + }, + "host": "<0>{{hostname}} adlı sunucuya bağlanıyorsunuz - lütfen hesap oluşturmadan önce sunucuya güvendiğinizi onaylayın", + "no": "Geri dön", + "title": "Bu sunucuya güveniyor musunuz?", + "yes": "Bu sunucuya güveniyorum" + }, + "verify": { + "description": "Kaydettiğinizi doğrulamak ve hesabınızı oluşturmak için önceki aşamada gösterilen şifreleme anahtarınızı girin", + "invalidData": "Veri geçersiz", + "noMatch": "Şifreleme anahtarı eşleşmiyor", + "passphraseLabel": "12 kelimelik şifreleme anahtarınız", + "recaptchaFailed": "ReCaptcha doğrulaması başarısız", + "register": "Hesap oluştur", + "title": "Şifreleme anahtarınızı doğrulayın" + } + }, + "errors": { + "badge": "Bir şeyler ters gitti", + "details": "Hata detayları", + "reloadPage": "Sayfayı yenile", + "showError": "Hata ayrıntılarını göster", + "title": "Bir hatayla karşılaştık!" + }, + "footer": { + "legal": { + "disclaimer": "Sorumluluk Reddi", + "disclaimerText": "sudo-flix herhangi bir dosya barındırmaz, yalnızca 3. parti hizmetlere bağlantı verir. Yasal meseleler, dosya barındırıcıları ve sağlayıcıları ile görüşülmelidir. sudo-flix, video sağlayıcıları tarafından gösterilen hiçbir medya dosyasından sorumlu değildir." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Bu açık kaynaklı akış uygulamasıyla en sevdiğiniz dizileri ve filmleri izleyin." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Hakkında", + "dmca": "DMCA", + "login": "Giriş yap", + "pagetitle": "{{title}} - sudo-flix", + "register": "Kayıt ol", + "settings": "Ayarlar" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Yerimleri" + }, + "continueWatching": { + "sectionTitle": "İzlemeye devam edin" + }, + "mediaList": { + "stopEditing": "Düzenlemeyi bırak" + }, + "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ı" + }, + "titles": { + "day": { + "default": "Bu öğleden sonra ne izlemek istersiniz?", + "extra": [ + "Maceraperest mi hissediyorsun? Jurassic Park iyi bir seçim olabilir." + ] + }, + "morning": { + "default": "Bu sabah ne izlemek istersiniz?", + "extra": ["Before Sunrise'a iyi diyorlar"] + }, + "night": { + "default": "Bu akşam ne izlemek istersiniz?", + "extra": ["Yoruldun mu? The Exorcist'e iyi diyorlar."] + } + } + }, + "media": { + "episodeDisplay": "S{{season}} B{{episode}}", + "types": { + "movie": "Film", + "show": "Dizi" + } + }, + "navigation": { + "banner": { + "offline": "İnternet bağlantınızı kontrol ediniz" + }, + "menu": { + "about": "Hakkımızda", + "donation": "Bağış yap", + "logout": "Çıkış yap", + "register": "Buluta eşitle", + "settings": "Ayarlar", + "support": "Destekle" + } + }, + "notFound": { + "badge": "Bulunamadı", + "goHome": "Ana sayfaya dön", + "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 dön" + }, + "casting": { + "enabled": "Cihaza yansıtılıyor..." + }, + "menus": { + "downloads": { + "disclaimer": "İndirme bağlantıları doğrudan sağlayıcının kendisinden alınır. sudo-flix'in sağlanan indirme bağlantıları üzerinde hiçbir konrolü yoktur.", + "downloadPlaylist": "Oynatma listesini indir", + "downloadSubtitle": "Geçerli altyazıyı indir", + "downloadVideo": "Videoyu indir", + "hlsDisclaimer": "İndirme bağlantıları doğrudan sağlayıcının kendisinden alınır. sudo-flix'in sağlanan indirme bağlantıları üzerinde hiçbir konrolü yoktur. Lütfen bir HLS oynatma listesi indirdiğinizi unutmayın; bu, gelişmiş multimedya akışına aşina olan kullanıcılara yöneliktir.", + "onAndroid": { + "1": "Android'e indirmek için önce indir butonuna basın, sonra açılan yeni sayfada video üzerine basılı tutun, ardından Videoyu indir seçeneğini seçin.", + "shortTitle": "İndir / Android", + "title": "Android'e indirme" + }, + "onIos": { + "1": "iOS'a indirmek için öncelikle indir butonuna basın, sonra açılan yeni sayfada 'e basın, hemen ardından Dosyalara Kaydet 'e basın.", + "shortTitle": "İndir / iOS", + "title": "iOS'a indirme" + }, + "onPc": { + "1": "Bilgisayarda önce indir butonuna tıklayın, sonra ise açılan yeni sayfada videoya sağ tıklayın ve Videoyu farklı kaydet seçeneğini seçin", + "shortTitle": "İndir / Bilgisayar", + "title": "Bilgisayara indirme" + }, + "title": "İndir" + }, + "episodes": { + "button": "Bölümler", + "emptyState": "Bu sezonda hiç bölüm yok, sonra tekrar deneyin!", + "episodeBadge": "B{{episode}}", + "loadingError": "Sezon yüklenirken hata oluştu", + "loadingList": "Yükleniyor...", + "loadingTitle": "Yükleniyor...", + "unairedEpisodes": "Bu sezondaki bir veya daha fazla bölüm henüz yayınlanmadığı için devre dışı bırakılmıştır." + }, + "playback": { + "speedLabel": "Oynatma hızı", + "title": "Oynatma ayarları" + }, + "quality": { + "automaticLabel": "Otomatik kalite", + "hint": "Farklı kalite seçenekleri elde etmek için <0>kaynak değiştirmeyi deneyebilirsiniz.", + "iosNoQuality": "Apple tarafından tanımlanan sınırlamalar nedeniyle, iOS'ta bu kaynak için kalite seçimi mevcut değildir. Farklı kalite seçenekleri elde etmek için <0>başka bir kaynağa geçmeyi deneyebilirsiniz.", + "title": "Kalite" + }, + "settings": { + "downloadItem": "İndir", + "enableSubtitles": "Altyazıları Etkinleştir", + "experienceSection": "İzleme deneyimi", + "playbackItem": "Oynatma ayarları", + "qualityItem": "Kalite", + "sourceItem": "Video kaynakları", + "subtitleItem": "Altyazı ayarları", + "videoSection": "Video ayarları" + }, + "sources": { + "failed": { + "text": "Video bulunmaya çalışılırken sorun oluştu, lütfen farklı bir kaynak deneyin.", + "title": "Video alınırken sorun oluştu" + }, + "noEmbeds": { + "text": "Herhangi bir video bulamadık, lütfen farklı bir kaynak deneyin.", + "title": "Video bulunamadı" + }, + "noStream": { + "text": "Bu kaynakta bu film ya da dizi için akış bulunmamaktadır.", + "title": "Akış yok" + }, + "title": "Kaynaklar", + "unknownOption": "Bilinmeyen" + }, + "subtitles": { + "customChoice": "Altyazı dosyası yükle", + "customizeLabel": "Seçenekler", + "offChoice": "Kapat", + "settings": { + "backlink": "Kişisel altyazılar", + "delay": "Altyazı gecikmesi", + "fixCapitals": "Büyük harf kullanımını düzelt" + }, + "title": "Altyazılar", + "unknownLanguage": "Bilinmeyen" + } + }, + "metadata": { + "api": { + "text": "API üstverisi yüklenemedi, lütfen internet bağlantınızı kontrol edin.", + "title": "API üstverisi yüklenemedi" + }, + "failed": { + "badge": "Başarısız oldu", + "homeButton": "Ana sayfaya dön", + "text": "Medyanın üstverisi TMDB'den alınamadı. Lütfen TMDB'nin göküp çökmediğini veya internet bağlantınız üzerinden engellenmediğini kontrol edin.", + "title": "Üstveri yüklenemedi" + }, + "notFound": { + "badge": "Bulunamadı", + "homeButton": "Ana sayfaya dön", + "text": "İstediğiniz medyayı bulamadık. URL'yi yanlış girdiniz ya da medya kaldırıldı.", + "title": "Medya bulunamadı." + } + }, + "nextEpisode": { + "cancel": "Vazgeç", + "next": "Sonraki bölüm" + }, + "playbackError": { + "badge": "Oynatma hatası", + "errors": { + "errorAborted": "Medyanın alınması kullanıcının isteği üzerine iptal edildi.", + "errorDecode": "Daha önce kullanılabilir olduğu belirlenmiş olmasına rağmen, medya kaynağının kodu çözülmeye çalışılırken bir hata oluştu ve bu da bir hataya neden oldu.", + "errorGenericMedia": "Bilinmeyen medya hatası oluştu.", + "errorNetwork": "Medya önceden mevcut olsa da başarıyla alınmasını engelleyen bir tür ağ hatası oluştu.", + "errorNotSupported": "Medya veya medya sağlayıcısı desteklenmiyor." + }, + "homeButton": "Ana sayfaya git", + "text": "Medya oynatılmaya çalışılırken bir hata oluştu. Lütfen tekrar deneyin.", + "title": "Video oynatılamadı!" + }, + "scraping": { + "items": { + "failure": "Hata oluştu", + "notFound": "Video mevcut değil", + "pending": "Videolar aranıyor..." + }, + "notFound": { + "badge": "Bulunamadı", + "detailsButton": "Ayrıntıları göster", + "homeButton": "Ana sayfaya git", + "text": "Sağlayıcılarımız arasında arama yaptık ve aradığınız medyayı bulamadık! Medyaları barındırmıyoruz ve mevcut olanlar üzerinde hiçbir kontrolümüz yok. Daha fazla ayrıntı için lütfen aşağıdaki 'Ayrıntıları göster' seçeneğine tıklayın.", + "title": "Bunu bulamadık" + } + }, + "time": { + "regular": "{{timeWatched}} / {{duration}}", + "remaining": "{{timeLeft}} kaldı • {{timeFinished, datetime}}'de bitiyor", + "shortRegular": "{{timeWatched}}", + "shortRemaining": "-{{timeLeft}}" + }, + "turnstile": { + "description": "Lütfen sağ taraftaki Captcha'yı çözerek insan olduğunuzu doğrulayın. Bu, sudo-flix'i güvende tutmak içindir!", + "error": "İnsan olduğunuz doğrulanamadı. Lütfen tekrar deneyin.", + "title": "İnsan olduğunuzu doğrulamamız gerekiyor.", + "verifyingHumanity": "İnsan olduğunuz doğrulanıyor..." + } + }, + "screens": { + "dmca": { + "text": "sudo-flix'in DMCA iletişim sayfasına hoş geldiniz! Fikri mülkiyet haklarına saygı duyuyoruz ve telif hakkıyla ilgili endişeleri hızlı bir şekilde ele almak istiyoruz. Telif hakkıyla korunan çalışmanızın platformumuzda uygunsuz şekilde kullanıldığını düşünüyorsanız lütfen aşağıdaki e-postaya ayrıntılı bir DMCA bildirimi gönderin. Lütfen telif hakkıyla korunan materyalin açıklamasını, iletişim bilgilerinizi ve iyi niyet beyanınızı ekleyin. Bu sorunları derhal çözmeye kararlıyız ve ayrıca sudo-flix'i yaratıcılığa ve telif haklarına saygılı bir yer olarak tutma konusundaki işbirliğiniz için teşekkür ederiz.", + "title": "DMCA" + }, + "loadingApp": "Uygulama yükleniyor", + "loadingUser": "Profiliniz yükleniyor", + "loadingUserError": { + "logout": "Çıkış yap", + "reset": "Özel sunucuyu sıfırla", + "text": "Profiliniz yüklenemedi", + "textWithReset": "Profiliniz özel sunucunuzdan yüklenemedi, varsayılan sunucuya sıfırlamak ister misiniz?" + }, + "migration": { + "failed": "Verileriniz taşınamadı.", + "inProgress": "Lütfen bekleyin, verilerinizi taşıyoruz. Bu çok uzun sürmez." + } + }, + "settings": { + "account": { + "accountDetails": { + "deviceNameLabel": "Cihaz adı", + "deviceNamePlaceholder": "Kişisel telefon", + "editProfile": "Düzenle", + "logoutButton": "Çıkış yap" + }, + "actions": { + "delete": { + "button": "Hesabı sil", + "confirmButton": "Hesabı sil", + "confirmDescription": "Hesabınızı silmek istediğinize emin misiniz? Tüm verileriniz kaybedilecek!", + "confirmTitle": "Emin misiniz?", + "text": "Bu işlem geri alınamaz. Tüm veriler silinir ve hiçbir şey kurtarılamaz.", + "title": "Hesabı sil" + }, + "title": "Eylemler" + }, + "devices": { + "deviceNameLabel": "Cihaz adı", + "failed": "Oturumlar yüklenemedi", + "removeDevice": "Sil", + "title": "Cihazlar" + }, + "profile": { + "finish": "Düzenlemeyi bitir", + "firstColor": "Birinci profil rengi", + "secondColor": "İkinci profil rengi", + "title": "Profil resmini düzenle", + "userIcon": "Kullanıcı simgesi" + }, + "register": { + "cta": "Hadi başlayalım", + "text": "İzleme ilerlemenizi cihazlar arasında paylaşın ve eşit tutun.", + "title": "Buluta eşitle" + }, + "title": "Hesap" + }, + "appearance": { + "activeTheme": "Aktif", + "themes": { + "blue": "Mavi", + "default": "Varsayılan", + "gray": "Gri", + "red": "Kırmızı", + "teal": "Turkuaz" + }, + "title": "Görünüm" + }, + "connections": { + "server": { + "description": "Verilerinizi depolamak için özel bir arkayüze bağlanmak istiyorsanız, bunu etkinleştirin ve URL'yi sağlayın. <0>Yönergeler.", + "label": "Özel sunucu", + "urlLabel": "Özel sunucu URL'si" + }, + "title": "Bağlantılar", + "workers": { + "addButton": "Yeni işleyici ekle", + "description": "Uygulamanın çalışması için tüm trafik vekil sunucular üzerinden yönlendirilir. Kendi işleyicilerinizi getirmek istiyorsanız bunu etkinleştirin.<0>Yönergeler.", + "emptyState": "Henüz işleyici yok, aşağıya bir tane ekleyin", + "label": "Özel vekil sunucu işleyici kullan", + "urlLabel": "İşleyici URL'leri", + "urlPlaceholder": "https://" + } + }, + "reset": "Sıfırla", + "save": "Kaydet", + "sidebar": { + "info": { + "appVersion": "Uygulama sürümü", + "backendUrl": "Arkayüz URL'si", + "backendVersion": "Arkayüz sürümü", + "hostname": "Ana makine adı", + "insecure": "Güvensiz", + "notLoggedIn": "Giriş yapmadınız", + "secure": "Güvenli", + "title": "Uygulama bilgisi", + "unknownVersion": "Bilinmeyen", + "userId": "Kullanıcı Kimliği" + } + }, + "subtitles": { + "backgroundLabel": "Arka plan opaklığı", + "colorLabel": "Renk", + "previewQuote": "Korkmamalıyım. Korku aklın katilidir.", + "textSizeLabel": "Yazı boyutu", + "title": "Altyazılar" + }, + "unsaved": "Kaydedilmemiş değişiklikleriniz mevcut" + } +} diff --git a/src/assets/locales/uk.json b/src/assets/locales/uk.json new file mode 100644 index 00000000..cef2eb22 --- /dev/null +++ b/src/assets/locales/uk.json @@ -0,0 +1,528 @@ +{ + "about": { + "description": "sudo-flix це веб-додаток, який шукає інтернет для стрімінгових відтворень. Команда прагне до мінімалістичного стилю в споживанні контенту.", + "faqTitle": "Загальні питання", + "q1": { + "body": "sudo-flix не зберігає жодного контенту. Коли ви натискаєте щось для перегляду, в Інтернеті виконується пошук вибраного медіа (на екрані завантаження та на вкладці «джерела відео» ви можете побачити, яке джерело ви використовуєте). Медіафайли ніколи не завантажуються за допомогою \"sudo-flix\", усе відбувається через цей механізм пошуку.", + "title": "Звідки береться вміст?" + }, + "q2": { + "body": "Неможливо подати запит на шоу чи фільм, оскільки sudo-flix не керує жодним вмістом. Весь контент переглядається через джерела в Інтернеті.", + "title": "Де я можу подати запит на шоу чи фільм?" + }, + "q3": { + "body": "Наші результати пошуку надходять від The Movie Database (TMDB) і відображаються незалежно від того, чи насправді є вміст у наших джерелах.", + "title": "Результати пошуку відображають шоу чи фільм, чому я не можу його відтворити?" + }, + "title": "Про sudo-flix" + }, + "actions": { + "copied": "Скопійовано", + "copy": "Копіювати" + }, + "auth": { + "createAccount": "У вас ще немає облікового запису? <0>Створити обліковий запис.", + "deviceNameLabel": "Ім'я пристрою", + "deviceNamePlaceholder": "Особистий телефон", + "generate": { + "description": "Ваша парольна фраза виступає як ваше ім'я користувача та пароль. Переконайтеся, що ви зберігаєте його в безпеці, оскільки вам буде потрібно вводити його для входу в свій обліковий запис", + "next": "Я зберіг(ла) свою парольну фразу", + "passphraseFrameLabel": "Парольна фраза", + "title": "Ваша парольна фраза" + }, + "hasAccount": "Вже є аккаунт? <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}} – підтвердьте, що довіряєте йому, перш ніж створювати обліковий запис", + "no": "Повернутися назад", + "title": "Чи довіряєте ви цьому серверу?", + "yes": "Я довіряю цьому серверу" + }, + "verify": { + "description": "Будь ласка, введіть свою парольну фразу, яка була раніше, щоб підтвердити її збереження та створити обліковий запис", + "invalidData": "Дані недійсні", + "noMatch": "Парольна фраза не збігається", + "passphraseLabel": "Ваша парольна фраза з 12 слів", + "recaptchaFailed": "Валідація капчі не вдалася", + "register": "Створити акаунт", + "title": "Підтвердьте свою парольну фразу" + } + }, + "errors": { + "badge": "Це зламалося", + "details": "Деталі помилки", + "reloadPage": "Перезавантажити сторінку", + "showError": "Показати деталі помилки", + "title": "Ми зіткнулися з помилкою!" + }, + "footer": { + "legal": { + "disclaimer": "Застереження", + "disclaimerText": "sudo-flix не містить жодних файлів, він лише посилається на сторонні служби. Юридичні питання слід вирішувати з хостами та провайдерами файлів. sudo-flix не несе відповідальності за будь-які медіафайли, показані постачальниками відео." + }, + "links": { + "discord": "Діскорд", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Дивіться свої улюблені шоу та фільми за допомогою цього відкритого стрімінгового додатка." + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "Про", + "dmca": "DMCA", + "login": "Логін", + "onboarding": "Встановлення", + "pagetitle": "{{title}} - sudo-flix", + "register": "Зареєструватися", + "settings": "Налаштування" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Закладки" + }, + "continueWatching": { + "sectionTitle": "Продовжити перегляд" + }, + "mediaList": { + "stopEditing": "Зупинити редагування" + }, + "search": { + "allResults": "Це все, що ми маємо!", + "failed": "Не вдалося знайти медіафайли, повторіть спробу!", + "loading": "Завантаження...", + "noResults": "Ми не змогли знайти нічого!", + "placeholder": "Що ви хочете подивитися?", + "sectionTitle": "Результати пошуку" + }, + "titles": { + "day": { + "default": "Що б ви хотіли подивитися сьогодні вдень?", + "extra": [ + "Хочете пригод? Парк Юрського періоду може стати ідеальним вибором." + ] + }, + "morning": { + "default": "Що б ви хотіли подивитися сьогодні вранці?", + "extra": ["Я чув, що \"Перед сходом сонця\" гарний"] + }, + "night": { + "default": "Що б ви хотіли подивитися сьогодні ввечері?", + "extra": ["Втомився? Я чув, що \"Екзорцист\" хороший."] + } + } + }, + "media": { + "episodeDisplay": "С{{season}} Е{{episode}}", + "types": { + "movie": "Фільм", + "show": "Шоу" + } + }, + "navigation": { + "banner": { + "offline": "Перевірте підключення до Інтернету" + }, + "menu": { + "about": "Про нас", + "donation": "Пожертвуйте", + "logout": "Вийти", + "register": "Синхронізація з хмарою", + "settings": "Налаштування", + "support": "Підтримка" + } + }, + "notFound": { + "badge": "Не знайдено", + "goHome": "На головну", + "message": "Ми шукали всюди: під смітниками, у шафі, за проксі-сервером, але зрештою не змогли знайти сторінку, яку ви шукали.", + "title": "Не вдалося знайти цю сторінку" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "Скасувати", + "confirm": "Використовувати налаштування за умовчанням", + "description": "Налаштування за замовчуванням не мають найкращих потоків і можуть бути нестерпно повільними.", + "title": "Ви впевнені?" + }, + "extension": { + "back": "Повернутись назад", + "explainer": "Використовуючи розширення для браузера, ви можете отримати найякісніші трансляції, які ми можемо запропонувати. Просто встановивши його.", + "extensionHelp": "Якщо ви встановили розширення, але воно не виявлено. Відкрийте розширення в меню розширень вашого браузеру і дотримуйтеся вказівок на екрані.", + "status": { + "disallowed": "Розширення не ввімкнено для цієї сторінки", + "disallowedAction": "Активувати розширення", + "failed": "Не вдалося отримати статус", + "loading": "Очікуємо, поки ви встановите розширення", + "outdated": "Версія розширення застаріла", + "success": "Розширення працює як очікувалося!" + }, + "submit": "Продовжити", + "title": "Почати використовувати розширення" + }, + "proxy": { + "back": "Повернутись назад", + "explainer": "З використанням проксі ви можете отримати високоякісні потоки, створивши самостійний проксі-сервіс.", + "input": { + "errorConnection": "Не вдалося підключитися до проксі", + "errorInvalidUrl": "Не валідний URL", + "errorNotProxy": "Очікувався проксі, але отримано вебсайт", + "label": "URL проксі", + "placeholder": "https://" + }, + "link": "Дізнайтесь як створити проксі", + "submit": "Надати проксі", + "title": "Давайте створимо новий проксі" + }, + "start": { + "explainer": "Щоб отримати найкращу трансляцію. Вам потрібно буде вибрати, який метод стрімінгу ви хочете використовувати.", + "options": { + "default": { + "text": "Мені не потрібна хороша якість потоків,<0 /> <1>використовувати налаштування за замовчуванням" + }, + "extension": { + "action": "Встановити розширення", + "description": "Встановіть розширення для браузера та отримайте доступ до найкращих джерел.", + "quality": "Найкраща якість", + "title": "Розширення браузера" + }, + "proxy": { + "action": "Налаштування проксі", + "description": "Налаштуйте проксі всього за 5 хвилин і отримайте доступ до чудових джерел.", + "quality": "Гарна якість", + "title": "Користувацький проксі" + } + }, + "title": "Давайте налаштуємо вам sudo-flix" + } + }, + "overlays": { + "close": "Закрити" + }, + "player": { + "back": { + "default": "На головну", + "short": "Назад" + }, + "casting": { + "enabled": "Трансляція на пристрій..." + }, + "menus": { + "downloads": { + "disclaimer": "Завантаження беруться безпосередньо від провайдера. У sudo-flix немає контролю над тим, як надаються завантаження.", + "downloadPlaylist": "Завантажити плейлист", + "downloadSubtitle": "Завантажити поточні субтитри", + "downloadVideo": "Завантажити відео", + "hlsDisclaimer": "Завантаження виконуються безпосередньо від постачальника. У sudo-flix немає контролю над тим, як надаються завантаження.

Зверніть увагу, що ви завантажуєте список відтворення HLS, його не рекомендується завантажувати, якщо ви не знайомі з розширеними форматами потокового передавання. Спробуйте різні джерела для інших форматів.", + "onAndroid": { + "1": "Щоб завантажити на Android, натисніть кнопку завантаження, потім на новій сторінці торкніться й утримуйте відео, а потім виберіть зберегти.", + "shortTitle": "Завантажити / Android", + "title": "Завантажити на Android" + }, + "onIos": { + "1": "Щоб завантажити на iOS, натисніть кнопку завантаження, далі на новій сторінці, натисніть , а потім Зберегти у файли .", + "shortTitle": "Завантажити / iOS", + "title": "Завантаження на iOS" + }, + "onPc": { + "1": "На комп'ютері натисніть кнопку завантаження, далі на новій вкладці клацніть правим кліком по відео та виберіть Зберегти відео як", + "shortTitle": "Завантажити / Комп'ютер", + "title": "Завантаження на комп'ютер" + }, + "title": "Завантажити" + }, + "episodes": { + "button": "Епізоди", + "emptyState": "У цьому сезоні немає серій, перевірте пізніше!", + "episodeBadge": "Е{{episode}}", + "loadingError": "Помилка завантаження сезону", + "loadingList": "Завантаження...", + "loadingTitle": "Завантаження...", + "unairedEpisodes": "Один або кілька епізодів у цьому сезоні недоступні, оскільки вони ще не вийшли в ефір." + }, + "playback": { + "speedLabel": "Швидкість відтворення", + "title": "Налаштування відтворення" + }, + "quality": { + "automaticLabel": "Якість автоматична", + "hint": "Спробуйте <0>переключити джерело, аби отримати інші варіанти якості.", + "iosNoQuality": "Через обмеження, встановлені Apple, вибір якості не доступний у системі iOS для цього джерела. Ви можете спробувати <0>змінити джерело, щоб отримати інші варіанти якості.", + "title": "Якість" + }, + "settings": { + "downloadItem": "Завантажити", + "enableSubtitles": "Увімкнути субтитри", + "experienceSection": "Враження від перегляду", + "playbackItem": "Налаштування відтворення", + "qualityItem": "Якість", + "sourceItem": "Джерела відео", + "subtitleItem": "Налаштування субтитрів", + "videoSection": "Налаштування відео" + }, + "sources": { + "failed": { + "text": "Під час спроби знайти будь-яке відео сталася помилка. Спробуйте інше джерело.", + "title": "Не вдалося отримати дані" + }, + "noEmbeds": { + "text": "Ми не змогли знайти жодних вставок. Будь ласка, спробуйте інше джерело.", + "title": "Не знайдено вбудованих елементів" + }, + "noStream": { + "text": "Це джерело не має потоків для цього фільму чи шоу.", + "title": "Немає потоку" + }, + "title": "Джерела", + "unknownOption": "Невідомий" + }, + "subtitles": { + "customChoice": "Виберіть субтитри з файлу", + "customizeLabel": "Налаштувати", + "offChoice": "Вимкнено", + "settings": { + "backlink": "Власні субтитри", + "delay": "Затримка субтитрів", + "fixCapitals": "Виправити використання великих літер" + }, + "title": "Субтитри", + "unknownLanguage": "Невідомий" + } + }, + "metadata": { + "api": { + "text": "Не вдалося завантажити метадані API, перевірте підключення до Інтернету.", + "title": "Не вдалося завантажити метадані API" + }, + "dmca": { + "badge": "Видалено", + "text": "Це медіа більше не доступне через повідомлення про видалення або позов про порушення авторських прав.", + "title": "Медіа було видалено" + }, + "extensionPermission": { + "badge": "Дозвіл Відсутній", + "button": "Використовувати розширення", + "text": "У вас вже є розширення для браузера, але нам потрібен ваш дозвіл, щоб почати використовувати його.", + "title": "Налаштуйте продовження" + }, + "failed": { + "badge": "Не вдалося", + "homeButton": "Повернутися на головну", + "text": "Не вдалося завантажити метадані мультимедіа з TMDB. Перевірте, чи не відбувається збій TMDB або чи не є заблоковано у вашому інтернет-з'єднанні.", + "title": "Не вдалося завантажити метадані" + }, + "notFound": { + "badge": "Не знайдено", + "homeButton": "Повернутися на головну", + "text": "Не вдалося знайти запитані мультимедіа. Вони або були вилучені, або ви змінили URL.", + "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}}" + }, + "turnstile": { + "description": "Будь ласка, переконайтеся, що ви людина, заповнивши капчу праворуч. Це робиться для безпеки sudo-flix!", + "error": "Не вдалося підтвердити вашу людяність. Будь ласка, спробуйте ще раз.", + "title": "Ми повинні переконатися, що ви людина.", + "verifyingHumanity": "Перевіряємо вашу людяність..." + } + }, + "screens": { + "dmca": { + "text": "Вітаємо на нашій сторінці зв'язку DMCA! Ми поважаємо права інтелектуальної власності і хочемо вирішити будь-які проблеми з авторськими правами швидко. Якщо ви вважаєте, що ваші авторські права були неправильно використані на нашій платформі, будь ласка, надішліть детальне повідомлення DMCA на електронну адресу нижче. Будь ласка, вкажіть опис авторського матеріалу, ваші контактні дані та заяву про добросовісну віру. Ми зобов'язані вирішити ці питання оперативно і вдячні за вашу співпрацю в збереженні sudo-flix місцем, яке поважає творчість та авторські права.", + "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": "Вигляд" + }, + "connections": { + "server": { + "description": "Якщо ви бажаєте підключитися до користувацького сервера для зберігання даних, увімкніть це та вкажіть URL-адресу. <0>Інструкції.", + "label": "Власний сервер", + "urlLabel": "URL сервера" + }, + "setup": { + "doSetup": "Виконайте налаштування", + "errorStatus": { + "description": "Здається, що один або декілька пунктів у цьому налаштуванні потребують вашої уваги.", + "title": "Дещо потребує вашої уваги" + }, + "itemError": "Щось не так із цією настройкою. Пройдіть налаштування ще раз, щоб виправити це.", + "items": { + "default": "Налаштування за замовчанням", + "extension": "Розширення", + "proxy": "Користувацький проксі" + }, + "redoSetup": "Повторити налаштування", + "successStatus": { + "description": "Усе готово для того, щоб ви могли почати дивитися улюблені медіа.", + "title": "Все готово!" + }, + "unsetStatus": { + "description": "Будь ласка, натисніть кнопку праворуч, щоб розпочати процес налаштування.", + "title": "Ви не завершили налаштування" + } + }, + "title": "З'єднання", + "workers": { + "addButton": "Додати нового працівника", + "description": "Щоб додаток працював, весь трафік маршрутизується через проксі-сервери. Увімкніть це, якщо ви хочете використовувати власні проксі воркери. <0>Інструкція.", + "emptyState": "Немає працівників", + "label": "Використовувати власних проксі-працівників", + "urlLabel": "URL-у працівника", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "Мова додатку", + "languageDescription": "Мова застосована до всього додатку.", + "thumbnail": "Створити мініатюри", + "thumbnailDescription": "Часто відео не мають мініатюр. Ви можете активувати цей параметр для їх генерації під час відтворення, але це може уповільнити відтворення відео.", + "thumbnailLabel": "Сгенерувати мініатюри", + "title": "Параметри" + }, + "reset": "Скинути налаштування", + "save": "Зберегти", + "sidebar": { + "info": { + "appVersion": "Версія застосунку", + "backendUrl": "URL сервера", + "backendVersion": "Версія сервера", + "hostname": "Ім'я хоста", + "insecure": "Небезпечно", + "notLoggedIn": "Ви не увійшли", + "secure": "Безпечно", + "title": "Інформація про застосунок", + "unknownVersion": "Невідомо", + "userId": "ID користувача" + } + }, + "subtitles": { + "backgroundLabel": "Прозорість фону", + "colorLabel": "Колір", + "previewQuote": "Я не повинен боятися. Страх вбиває розум.", + "textSizeLabel": "Розмір шрифту", + "title": "Субтитри" + }, + "unsaved": "У вас є незбережені зміни" + } +} diff --git a/src/assets/locales/vi.json b/src/assets/locales/vi.json new file mode 100644 index 00000000..19206815 --- /dev/null +++ b/src/assets/locales/vi.json @@ -0,0 +1,160 @@ +{ + "about": { + "description": "sudo-flix là một ứng dụng web tìm kiếm các nguồn truyền phát trực tuyến trên Internet. Nhóm phát triển ứng dụng này nhắm đến một cách dễ dàng hơn trong việc tiêu thụ nội dung.", + "faqTitle": "Các câu hỏi thường gặp", + "q1": { + "body": "sudo-flix không lưu trữ bất kì nội dung nào. Khi bạn chọn xem một nội dung nào đó, ứng dụng sẽ tìm kiếm nội dung đó trên Internet (Khi nội dung tải và trong tab 'nguồn video' bạn sẽ tìm thấy nguồn đang được dùng). Nội dung không bao giờ được tải lên trên sudo-flix, mọi thứ đều được tìm kiếm thông qua phương thức này.", + "title": "Nội dung đến từ đâu?" + }, + "q2": { + "body": "Việc yêu cầu thêm một chương trình truyền hình hoặc phim là điều bất khả thi bởi vì sudo-flix không quản lý bất kỳ nội dung nào. Tất cả nội dung được truyền thông qua những nguồn trên internet.", + "title": "Tôi có thể yêu cầu thêm một chương trình truyền hình hoặc phim ở đâu?" + }, + "q3": { + "body": "Các kết quả tìm kiếm được cung cấp bởi The Movie Database (TMDB) và hiện lên bất kể các nguồn của trang thực sự có lưu trữ nội dung đó hay không.", + "title": "Tại sao kết quả tìm kiếm hiển thị chương trình truyền hình hoặc phim nhưng tôi không thể xem nó?" + }, + "title": "Về sudo-flix" + }, + "actions": { + "copied": "Đã sao chép", + "copy": "Sao chép" + }, + "auth": { + "createAccount": "Chưa có tài khoản? <0>Hãy tạo tài khoản.", + "deviceNameLabel": "Tên thiết bị", + "deviceNamePlaceholder": "Điện thoại cá nhân", + "generate": { + "description": "Mật ngữ của bạn đóng vai trò là tên người dùng và mật khẩu của bạn. Hãy giữ nó an toàn vì bạn sẽ cần nhập nó để đăng nhập vào tài khoản của bạn", + "next": "Tôi đã lưu giữ mật ngữ của mình", + "passphraseFrameLabel": "Mật ngữ", + "title": "Mật ngữ của bạn" + }, + "hasAccount": "Đã có tài khoản? <0>Hãy đăng nhập.", + "login": { + "description": "Hãy nhập mật ngữ của bạn để đăng nhập vào tài khoản", + "deviceLengthError": "Hãy nhập tên thiết bị", + "passphraseLabel": "Mật ngữ gồm 12 từ", + "passphrasePlaceholder": "Mật ngữ", + "submit": "Đăng nhập", + "title": "Đăng nhập vào tài khoản của bạn", + "validationError": "Mật ngữ không chính xác hoặc không đầy đủ" + }, + "register": { + "information": { + "color1": "Màu hồ sơ số một", + "color2": "Màu hồ sơ số hai", + "header": "Hãy nhập tên cho thiết bị của bạn và chọn màu sắc và biểu tượng người dùng", + "icon": "Biểu tượng người dùng", + "next": "Tiếp theo", + "title": "Thông tin tài khoản" + } + }, + "trust": { + "failed": { + "text": "Bạn đã cài đặt nó một cách chính xác chưa?", + "title": "Không thể truy vấn máy chủ" + }, + "host": "Bạn đang kết nối đến máy chủ <0>{{hostname}} - vui lòng chắc chắn rằng bạn tin tưởng máy chủ này trước khi tạo tài khoản", + "no": "Quay lại", + "title": "Bạn có tin tưởng máy chủ này không?", + "yes": "Tôi tin tưởng máy chủ này" + }, + "verify": { + "description": "Vui lòng nhập mật ngữ của bạn lúc nãy đễ chắc chắn rằng bạn đã lưu nó và để tạo tài khoản", + "invalidData": "Dữ liệu không hợp lệ", + "noMatch": "Mật ngữ không khớp", + "passphraseLabel": "Mật ngữ 12 ký tự của bạn", + "recaptchaFailed": "Xác minh bằng ReCaptcha không hợp lệ", + "register": "Tạo tài khoản", + "title": "Nhập lại mật ngữ của bạn" + } + }, + "errors": { + "badge": "Lỗi", + "details": "Thông tin về lỗi", + "reloadPage": "Tải lại trang", + "showError": "Hiển thị thông tin về lỗi", + "title": "Đã xảy ra lỗi!" + }, + "footer": { + "legal": { + "disclaimer": "Tuyên bố miễn trừ trách nhiệm", + "disclaimerText": "sudo-flix không lưu trữ bất kì file nào, nó chỉ đến những đường dẫn của các dịch vụ bên thứ ba. Bất kỳ vấn đề nào về pháp lý nên được đưa đến chủ sỡ hữu của file hoặc những nhà cung cấp đó. sudo-flix hoàn toàn không chịu trách nhiệm cho bất kỳ nội dung nào được chiếu từ các nhà cung cấp." + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "Xem các chương trình và phim yêu thích của bạn với ứng dụng phát trực tuyến nguồn mở này." + }, + "global": { + "name": "sudo-flix", + "pages": { + "pagetitle": "{{title}} - sudo-flix", + "register": "Đăng ký" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "Đánh dấu" + }, + "continueWatching": { + "sectionTitle": "Tiếp tục xem" + }, + "search": { + "allResults": "Đó là tất cả chúng tôi có!", + "failed": "Không thể tìm thấy nội dung, hãy thử lại!", + "loading": "Đang tải...", + "noResults": "Chúng tôi không thể tìm thấy gì!", + "placeholder": "Bạn muốn xem gì?", + "sectionTitle": "Kết quả tìm kiếm" + } + }, + "media": { + "episodeDisplay": "M{{season}} T{{episode}}", + "types": { + "movie": "Phim", + "show": "Chương trình truyền hình" + } + }, + "navigation": { + "banner": { + "offline": "Hãy kiểm tra kết nối Internet của bạn" + } + }, + "notFound": { + "badge": "Không tìm thấy", + "goHome": "Quay lại trang chính", + "message": "Chúng tôi đã tìm kiếm khắp nơi: dưới thùng rác, trong tủ quần áo, đằng sau máy chủ proxy nhưng vẫn không thể tìm thấy trang bạn đang tìm kiếm.", + "title": "Không thể tìm thấy trang" + }, + "player": { + "back": { + "default": "Quay lại trang chính", + "short": "Quay lại" + }, + "menus": { + "episodes": { + "button": "Tập", + "loadingList": "Đang tải...", + "loadingTitle": "Đang tải..." + }, + "sources": { + "title": "Nguồn" + } + }, + "metadata": { + "notFound": { + "badge": "Không tìm thấy", + "homeButton": "Quay lại trang chính", + "text": "Chúng tôi không thể tìm thấy nội dung mà bạn yêu cầu. Hoặc là nó đã bị xóa, hoặc bạn đã xáo trộn URL.", + "title": "Không thể tìm thấy nội dung." + } + }, + "playbackError": { + "title": "Rất tiếc, đã hỏng!" + } + } +} diff --git a/src/assets/locales/zh-Hant.json b/src/assets/locales/zh-Hant.json new file mode 100644 index 00000000..088c2ce5 --- /dev/null +++ b/src/assets/locales/zh-Hant.json @@ -0,0 +1,102 @@ +{ + "about": { + "description": "sudo-flix 是一款在互聯網上搜尋流媒體的網路應用程式。團隊致力於讓使用者以最簡約的方式消費內容。", + "faqTitle": "常見問題", + "q1": { + "body": "sudo-flix 不託管任何內容。您點選觀看內容時,系統均從互聯網搜尋(在加載提示頁和“視頻源”選項卡中,您可以看到正在使用的源)。媒體從未在 sudo-flix 中上傳,所有內容均通過搜索機制而得。", + "title": "內容來自哪裡?" + } + }, + "auth": { + "login": { + "submit": "登入" + } + }, + "global": { + "pages": { + "about": "關於", + "login": "登入", + "settings": "設定" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "書籤" + }, + "search": { + "loading": "載入中..." + } + }, + "media": { + "types": { + "movie": "電影", + "show": "節目" + } + }, + "navigation": { + "menu": { + "donation": "捐", + "settings": "設定", + "support": "支援" + } + }, + "overlays": { + "close": "關閉" + }, + "player": { + "back": { + "short": "後退" + }, + "menus": { + "downloads": { + "title": "下載" + }, + "episodes": { + "loadingList": "載入中...", + "loadingTitle": "載入中..." + }, + "settings": { + "downloadItem": "下載" + }, + "subtitles": { + "title": "字幕" + } + }, + "metadata": { + "failed": { + "badge": "失敗" + } + }, + "nextEpisode": { + "cancel": "取消" + } + }, + "screens": { + "loadingUserError": { + "logout": "登出" + } + }, + "settings": { + "account": { + "title": "帳戶" + }, + "appearance": { + "themes": { + "blue": "藍色", + "gray": "灰色", + "red": "紅色", + "teal": "青色" + } + }, + "sidebar": { + "info": { + "insecure": "不安全", + "secure": "安全" + } + }, + "subtitles": { + "colorLabel": "顏色", + "title": "字幕" + } + } +} diff --git a/src/assets/locales/zh.json b/src/assets/locales/zh.json new file mode 100644 index 00000000..b2352d2e --- /dev/null +++ b/src/assets/locales/zh.json @@ -0,0 +1,526 @@ +{ + "about": { + "description": "sudo-flix 是一款在互联网上搜寻流媒体的网络应用程序。团队致力于让用户采取最简约的方式消费内容。", + "faqTitle": "常见问题", + "q1": { + "body": "sudo-flix 不托管任何内容。您点选观看内容时,系统均从互联网搜寻(在加载提示页和“视频源”选项卡中,您可以看到正在使用的源)。媒体从未在 sudo-flix 中上传,所有内容均通过搜索机制而得。", + "title": "内容来自哪里?" + }, + "q2": { + "body": "无法主动请求影视剧或其他节目,sudo-flix 不管理任何内容。所有内容均从互联网视频源获取并供您观看。", + "title": "我可以从哪里请求观看影视剧或其他节目?" + }, + "q3": { + "body": "我们的搜索结果由电影数据库(TMDB)驱动,无论视频源是否有对应内容,均会显示结果。", + "title": "搜索结果中已显示了影视剧或其他节目,为何我无法播放?" + }, + "title": "关于 sudo-flix" + }, + "actions": { + "copied": "已复制", + "copy": "复制" + }, + "auth": { + "createAccount": "还没有账户? <0>创建一个", + "deviceNameLabel": "设备名称", + "deviceNamePlaceholder": "如:我的手机", + "generate": { + "description": "您的密码短语相当于用户名与密码。由于您需要输入它来登录账户,请确保将其存放到安全位置", + "next": "我已保存密码短语", + "passphraseFrameLabel": "密码短语", + "title": "您的密码短语" + }, + "hasAccount": "已经拥有账户?<0>点击此处登录。", + "login": { + "description": "请输入密码短语以登录您的账户", + "deviceLengthError": "请输入设备名称", + "passphraseLabel": "12 词密码短语", + "passphrasePlaceholder": "密码短语", + "submit": "登录", + "title": "登录您的账户", + "validationError": "密码短语不正确或不完整" + }, + "register": { + "information": { + "color1": "头像配色 1", + "color2": "头像配色 2", + "header": "为您的设备输入名称,并选取一组代表色和一个用户图标", + "icon": "用户图标", + "next": "下一步", + "title": "账户信息" + } + }, + "trust": { + "failed": { + "text": "您的配置是否正确?", + "title": "服务器无法接通" + }, + "host": "您正在连接到 <0>{{hostname}} - 在创建账户前,确保您信任它", + "no": "返回", + "title": "您是否信任这个服务器?", + "yes": "我信任这个服务器" + }, + "verify": { + "description": "请输入早先的密码短语,以确认您已经保存它,并创建您的账户", + "invalidData": "数据无效", + "noMatch": "密码短语不匹配", + "passphraseLabel": "您的 12 词密码短语", + "recaptchaFailed": "ReCaptcha 验证失败", + "register": "创建账户", + "title": "确认您的密码短语" + } + }, + "errors": { + "badge": "它损坏了", + "details": "错误细节", + "reloadPage": "刷新页面", + "showError": "显示错误细节", + "title": "我们遇到了错误!" + }, + "footer": { + "legal": { + "disclaimer": "免责声明", + "disclaimerText": "sudo-flix 不托管任何文件,仅链接到第三方服务。 法律问题应由文件托管者和内容提供者解决。 sudo-flix 对视频提供者显示的任何媒体文件不承担任何责任。" + }, + "links": { + "discord": "Discord", + "dmca": "DMCA", + "github": "GitHub" + }, + "tagline": "在这个开源流媒体应用上观看你最喜爱的影视剧或其他节目。" + }, + "global": { + "name": "sudo-flix", + "pages": { + "about": "关于", + "dmca": "DMCA", + "login": "登录", + "onboarding": "设定", + "pagetitle": "{{title}} - sudo-flix", + "register": "注册", + "settings": "设置" + } + }, + "home": { + "bookmarks": { + "sectionTitle": "书签" + }, + "continueWatching": { + "sectionTitle": "继续观看" + }, + "mediaList": { + "stopEditing": "停止编辑" + }, + "search": { + "allResults": "以上是我们能找到的所有结果!", + "failed": "查找媒体失败,请重试!", + "loading": "载入中……", + "noResults": "我们找不到任何结果!", + "placeholder": "您想看些什么?", + "sectionTitle": "搜索结果" + }, + "titles": { + "day": { + "default": "您今天下午想看什么?", + "extra": ["想要来场冒险?《侏罗纪公园》可能是最佳选项。"] + }, + "morning": { + "default": "您今早想看什么?", + "extra": ["我听说《爱在黎明破晓前》不错"] + }, + "night": { + "default": "您今晚想看什么?", + "extra": ["累了?我听说《驱魔人》不错。"] + } + } + }, + "media": { + "episodeDisplay": "第{{season}}季 第{{episode}}集", + "types": { + "movie": "电影", + "show": "电视节目" + } + }, + "navigation": { + "banner": { + "offline": "检查您的互联网连接" + }, + "menu": { + "about": "关于我们", + "donation": "捐赠", + "logout": "登出", + "register": "同步到云端", + "settings": "设置", + "support": "支持" + } + }, + "notFound": { + "badge": "未找到", + "goHome": "返回首页", + "message": "我们已经到处找过了:不管是垃圾桶下、橱柜里或是代理之后。但最终并没有发现您查找的页面。", + "title": "无法找到页面" + }, + "onboarding": { + "defaultConfirm": { + "cancel": "取消", + "confirm": "使用默认设定", + "description": "默认设定不具备最好的视频流,并且播放速度可能慢得难以忍受。", + "title": "您确定吗?" + }, + "extension": { + "back": "返回", + "explainer": "使用浏览器扩展,您可以获得我们提供的最佳串流。只需简单的安装即可。", + "extensionHelp": "如您已安装扩展程序但未被检测到,请从浏览器的扩展菜单打开扩展 并按屏幕上的指示操作。", + "status": { + "disallowed": "扩展程序未对本页面启用", + "disallowedAction": "启用扩展程序", + "failed": "请求状态失败", + "loading": "正等待您安装扩展程序", + "outdated": "扩展版本过旧", + "success": "扩展程序工作正常!" + }, + "submit": "继续", + "title": "让我们从扩展程序开始" + }, + "proxy": { + "back": "返回", + "explainer": "使用代理方式,您可以通过自助代理服务获得高质量的串流。", + "input": { + "errorConnection": "无法连接到代理", + "errorInvalidUrl": "URL 无效", + "errorNotProxy": "检测到常规网站,链接应指向代理", + "label": "代理 URL", + "placeholder": "https://" + }, + "link": "了解如何设置代理", + "submit": "提交代理", + "title": "让我们设置新的代理" + }, + "start": { + "explainer": "为了尽可能获得最佳串流,您将需要选择使用何种串流方法。", + "options": { + "default": { + "text": "我不想使用高质量串流,<0 /> <1>使用默认设定" + }, + "extension": { + "action": "安装扩展程序", + "description": "安装浏览器扩展并获取最佳视频源。", + "quality": "最佳质量", + "title": "浏览器扩展" + }, + "proxy": { + "action": "设定代理", + "description": "在 5 分钟内设定好代理并获取优质资源。", + "quality": "高质量", + "title": "自定义代理" + } + }, + "title": "让我们为您设定 sudo-flix" + } + }, + "overlays": { + "close": "关闭" + }, + "player": { + "back": { + "default": "返回首页", + "short": "返回" + }, + "casting": { + "enabled": "正在投放到设备…" + }, + "menus": { + "downloads": { + "disclaimer": "下载内容是直接从内容提供者获取的。sudo-flix 无法控制下载内容如何被提供。", + "downloadPlaylist": "下载播放列表", + "downloadSubtitle": "下载当前字幕", + "downloadVideo": "下载视频", + "hlsDisclaimer": "下载内容是直接从内容提供者获取的。sudo-flix 无法控制下载内容如何被提供。

请注意,您正在下载 HLS 播放列表,如您不熟悉高级流媒体格式,我们不推荐此操作。如需下载其他格式,请尝试切换视频源。", + "onAndroid": { + "1": "要从 Android 下载,先点击下载按钮,之后在新的页面上, 点击并按住视频,然后选择 保存。", + "shortTitle": "下载 / Android", + "title": "正在 Android 上下载" + }, + "onIos": { + "1": "要从 iOS 下载,点击下载按钮,之后在新的页面上,点击 ,然后保存到文件 。", + "shortTitle": "下载 / iOS", + "title": "正在 iOS 上下载" + }, + "onPc": { + "1": "在 PC 上,点击下载按钮,之后在新的页面上,右击视频并选择 另存视频为", + "shortTitle": "下载 / PC", + "title": "正在 PC 上下载" + }, + "title": "下载" + }, + "episodes": { + "button": "分集", + "emptyState": "该季暂无剧集,请稍后再来!", + "episodeBadge": "第{{episode}}集", + "loadingError": "加载分季时发生错误", + "loadingList": "载入中……", + "loadingTitle": "载入中……", + "unairedEpisodes": "本季中的一集或多集已因尚未播出而被禁用。" + }, + "playback": { + "speedLabel": "播放速度", + "title": "播放设置" + }, + "quality": { + "automaticLabel": "自动质量", + "hint": "您可以尝试<0>切换视频源以获取不同的质量选项。", + "iosNoQuality": "由于苹果施加的限制,该视频源的质量选择在 iOS 上不可用。您可以尝试<0>切换到其他视频源以获取不同的质量选项。", + "title": "质量" + }, + "settings": { + "downloadItem": "下载", + "enableSubtitles": "启用字幕", + "experienceSection": "观看体验", + "playbackItem": "播放设置", + "qualityItem": "质量", + "sourceItem": "视频源", + "subtitleItem": "字幕设置", + "videoSection": "视频设置" + }, + "sources": { + "failed": { + "text": "尝试获取任何视频时均发生错误,请尝试其他视频源。", + "title": "刮取失败" + }, + "noEmbeds": { + "text": "我们无法找到任何嵌入内容,请尝试其他视频源。", + "title": "未找到嵌入内容" + }, + "noStream": { + "text": "此视频源没有该影片或节目的串流。", + "title": "没有流" + }, + "title": "视频源", + "unknownOption": "未知" + }, + "subtitles": { + "customChoice": "从文件选取字幕", + "customizeLabel": "自定义", + "offChoice": "关闭", + "settings": { + "backlink": "自定义字幕", + "delay": "字幕延时", + "fixCapitals": "修整大小写" + }, + "title": "字幕", + "unknownLanguage": "未知" + } + }, + "metadata": { + "api": { + "text": "无法载入 API 元数据,请检查您的互联网连接。", + "title": "载入 API 元数据失败" + }, + "dmca": { + "badge": "已移除", + "text": "由于收到删除通知或版权声索,此媒体不再可用。", + "title": "媒体已被移除" + }, + "extensionPermission": { + "badge": "缺失权限", + "button": "使用扩展程序", + "text": "您已安装浏览器扩展,但我们需要您的许可才能启用它。", + "title": "配置扩展程序" + }, + "failed": { + "badge": "失败", + "homeButton": "返回首页", + "text": "无法从 TMDB 载入媒体的元数据。请检查 TMDB 是否掉线或在您的网络连接中被屏蔽。", + "title": "载入元数据失败" + }, + "notFound": { + "badge": "未找到", + "homeButton": "返回首页", + "text": "我们无法找到您请求的媒体。它可能已被删除,或您篡改了 URL.", + "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}}" + }, + "turnstile": { + "description": "请完成右侧的验证码以验证您是人类。这是为了确保 sudo-flix 的安全!", + "error": "验证失败。请重试。", + "title": "我们需要验证您是人类。", + "verifyingHumanity": "正在验证您是否为人类…" + } + }, + "screens": { + "dmca": { + "text": "欢迎来到 sudo-flix 的 DMCA 联系页面!我们尊重知识产权,并希望迅速解决任何版权问题。如果您认为您的版权作品在我们的平台上被不当使用,请发送详细的 DMCA 通知至以下邮箱。同时请附上受版权保护的材料的描述、您的具体联系方式以及善意信念声明。 我们承诺及时解决这些问题,并感谢您的合作,让 sudo-flix 成为尊重创意和版权之地。", + "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": "个人资料颜色 1", + "secondColor": "个人资料颜色 2", + "title": "编辑个人资料图像", + "userIcon": "用户图标" + }, + "register": { + "cta": "开始使用", + "text": "在设备之间共享并持续同步您的观看进度。", + "title": "同步到云端" + }, + "title": "账户" + }, + "appearance": { + "activeTheme": "已激活", + "themes": { + "blue": "蓝色", + "default": "默认", + "gray": "灰色", + "red": "红色", + "teal": "青色" + }, + "title": "外观" + }, + "connections": { + "server": { + "description": "若您想连接到自定义后端保存数据,请启用此选项并提供 URL。 <0>查看指引", + "label": "自定义服务器", + "urlLabel": "自定义服务器 URL" + }, + "setup": { + "doSetup": "进行设定", + "errorStatus": { + "description": "看起来此配置中的一项或多项需要您的注意。", + "title": "有件事需要您的注意" + }, + "itemError": "此设置存在错误。再次进行设定过程以修复它。", + "items": { + "default": "默认设定", + "extension": "扩展程序", + "proxy": "自定义代理" + }, + "redoSetup": "重新进行设定", + "successStatus": { + "description": "一切已经就绪,您可以开始观赏喜爱的媒体了。", + "title": "一切就绪!" + }, + "unsetStatus": { + "description": "请单击右侧的按钮开始进行设定。", + "title": "您尚未完成设定" + } + }, + "title": "连接", + "workers": { + "addButton": "添加新的 Worker", + "description": "要让应用程序正常运作,所有流量会通过代理路由。若您想使用自己的 Worker,请启用该选项。 <0>查看指引", + "emptyState": "还没有 Worker,在下方添加一个", + "label": "使用自定义代理 Worker", + "urlLabel": "Worker URL", + "urlPlaceholder": "https://" + } + }, + "preferences": { + "language": "应用程序语言", + "languageDescription": "作用于整个应用程序的语言。", + "thumbnail": "生成缩略图", + "thumbnailDescription": "大多数时候,视频没有缩略图。您可以启用此设置以在视频播放时动态生成它们,但此选项可能会拖慢视频速度。", + "thumbnailLabel": "生成缩略图", + "title": "偏好设置" + }, + "reset": "重设", + "save": "保存", + "sidebar": { + "info": { + "appVersion": "应用版本", + "backendUrl": "后端 URL", + "backendVersion": "后端版本", + "hostname": "主机名", + "insecure": "不安全", + "notLoggedIn": "您尚未登录", + "secure": "安全", + "title": "应用信息", + "unknownVersion": "未知", + "userId": "用户 ID" + } + }, + "subtitles": { + "backgroundLabel": "背景不透明度", + "colorLabel": "颜色", + "previewQuote": "我不能害怕。恐惧是心灵的杀手。", + "textSizeLabel": "字体大小", + "title": "字幕" + }, + "unsaved": "您有未保存的更改" + } +} diff --git a/src/assets/templates/opensearch.xml.hbs b/src/assets/templates/opensearch.xml.hbs new file mode 100644 index 00000000..a629d4dc --- /dev/null +++ b/src/assets/templates/opensearch.xml.hbs @@ -0,0 +1,6 @@ + + movie-web + The place for your favorite movies & shows + UTF-8 + + \ No newline at end of file diff --git a/src/backend/accounts/auth.ts b/src/backend/accounts/auth.ts new file mode 100644 index 00000000..71b23173 --- /dev/null +++ b/src/backend/accounts/auth.ts @@ -0,0 +1,35 @@ +import { ofetch } from "ofetch"; + +export interface SessionResponse { + id: string; + userId: string; + createdAt: string; + accessedAt: string; + device: string; + userAgent: string; +} +export interface LoginResponse { + session: SessionResponse; + token: string; +} + +export function getAuthHeaders(token: string): Record { + return { + authorization: `Bearer ${token}`, + }; +} + +export async function accountLogin( + url: string, + id: string, + deviceName: string, +): Promise { + return ofetch("/auth/login", { + method: "POST", + body: { + id, + device: deviceName, + }, + baseURL: url, + }); +} diff --git a/src/backend/accounts/bookmarks.ts b/src/backend/accounts/bookmarks.ts new file mode 100644 index 00000000..79495415 --- /dev/null +++ b/src/backend/accounts/bookmarks.ts @@ -0,0 +1,64 @@ +import { ofetch } from "ofetch"; + +import { getAuthHeaders } from "@/backend/accounts/auth"; +import { BookmarkResponse } from "@/backend/accounts/user"; +import { AccountWithToken } from "@/stores/auth"; +import { BookmarkMediaItem } from "@/stores/bookmarks"; + +export interface BookmarkMetaInput { + title: string; + year: number; + poster?: string; + type: string; +} + +export interface BookmarkInput { + tmdbId: string; + meta: BookmarkMetaInput; +} + +export function bookmarkMediaToInput( + tmdbId: string, + item: BookmarkMediaItem, +): BookmarkInput { + return { + meta: { + title: item.title, + type: item.type, + poster: item.poster, + year: item.year ?? 0, + }, + tmdbId, + }; +} + +export async function addBookmark( + url: string, + account: AccountWithToken, + input: BookmarkInput, +) { + return ofetch( + `/users/${account.userId}/bookmarks/${input.tmdbId}`, + { + method: "POST", + headers: getAuthHeaders(account.token), + baseURL: url, + body: input, + }, + ); +} + +export async function removeBookmark( + url: string, + account: AccountWithToken, + id: string, +) { + return ofetch<{ tmdbId: string }>( + `/users/${account.userId}/bookmarks/${id}`, + { + method: "DELETE", + headers: getAuthHeaders(account.token), + baseURL: url, + }, + ); +} diff --git a/src/backend/accounts/crypto.ts b/src/backend/accounts/crypto.ts new file mode 100644 index 00000000..9fe51380 --- /dev/null +++ b/src/backend/accounts/crypto.ts @@ -0,0 +1,131 @@ +import { pbkdf2Async } from "@noble/hashes/pbkdf2"; +import { sha256 } from "@noble/hashes/sha256"; +import { generateMnemonic, validateMnemonic } from "@scure/bip39"; +import { wordlist } from "@scure/bip39/wordlists/english"; +import forge from "node-forge"; + +type Keys = { + privateKey: Uint8Array; + publicKey: Uint8Array; + seed: Uint8Array; +}; + +async function seedFromMnemonic(mnemonic: string) { + return pbkdf2Async(sha256, mnemonic, "mnemonic", { + c: 2048, + dkLen: 32, + }); +} + +export function verifyValidMnemonic(mnemonic: string) { + return validateMnemonic(mnemonic, wordlist); +} + +export async function keysFromMnemonic(mnemonic: string): Promise { + const seed = await seedFromMnemonic(mnemonic); + + const { privateKey, publicKey } = forge.pki.ed25519.generateKeyPair({ + seed, + }); + + return { + privateKey, + publicKey, + seed, + }; +} + +export function genMnemonic(): string { + return generateMnemonic(wordlist); +} + +export async function signCode( + code: string, + privateKey: Uint8Array, +): Promise { + return forge.pki.ed25519.sign({ + encoding: "utf8", + message: code, + privateKey, + }); +} + +export function bytesToBase64(bytes: Uint8Array) { + return forge.util.encode64(String.fromCodePoint(...bytes)); +} + +export function bytesToBase64Url(bytes: Uint8Array): string { + return bytesToBase64(bytes) + .replace(/\//g, "_") + .replace(/\+/g, "-") + .replace(/=+$/, ""); +} + +export async function signChallenge(keys: Keys, challengeCode: string) { + const signature = await signCode(challengeCode, keys.privateKey); + return bytesToBase64Url(signature); +} + +export function base64ToBuffer(data: string) { + return forge.util.binary.base64.decode(data); +} + +export function base64ToStringBuffer(data: string) { + return forge.util.createBuffer(base64ToBuffer(data)); +} + +export function stringBufferToBase64(buffer: forge.util.ByteStringBuffer) { + return forge.util.encode64(buffer.getBytes()); +} + +export async function encryptData(data: string, secret: Uint8Array) { + if (secret.byteLength !== 32) + throw new Error("Secret must be at least 256-bit"); + + const iv = await new Promise((resolve, reject) => { + forge.random.getBytes(16, (err, bytes) => { + if (err) reject(err); + resolve(bytes); + }); + }); + + const cipher = forge.cipher.createCipher( + "AES-GCM", + forge.util.createBuffer(secret), + ); + cipher.start({ + iv, + tagLength: 128, + }); + cipher.update(forge.util.createBuffer(data, "utf8")); + cipher.finish(); + + const encryptedData = cipher.output; + const tag = cipher.mode.tag; + + return `${forge.util.encode64(iv)}.${stringBufferToBase64( + encryptedData, + )}.${stringBufferToBase64(tag)}` as const; +} + +export function decryptData(data: string, secret: Uint8Array) { + if (secret.byteLength !== 32) throw new Error("Secret must be 256-bit"); + + const [iv, encryptedData, tag] = data.split("."); + + const decipher = forge.cipher.createDecipher( + "AES-GCM", + forge.util.createBuffer(secret), + ); + decipher.start({ + iv: base64ToStringBuffer(iv), + tag: base64ToStringBuffer(tag), + tagLength: 128, + }); + decipher.update(base64ToStringBuffer(encryptedData)); + const pass = decipher.finish(); + + if (!pass) throw new Error("Error decrypting data"); + + return decipher.output.toString(); +} diff --git a/src/backend/accounts/import.ts b/src/backend/accounts/import.ts new file mode 100644 index 00000000..3caaffb3 --- /dev/null +++ b/src/backend/accounts/import.ts @@ -0,0 +1,33 @@ +import { ofetch } from "ofetch"; + +import { getAuthHeaders } from "@/backend/accounts/auth"; +import { AccountWithToken } from "@/stores/auth"; + +import { BookmarkInput } from "./bookmarks"; +import { ProgressInput } from "./progress"; + +export function importProgress( + url: string, + account: AccountWithToken, + progressItems: ProgressInput[], +) { + return ofetch(`/users/${account.userId}/progress/import`, { + method: "PUT", + body: progressItems, + baseURL: url, + headers: getAuthHeaders(account.token), + }); +} + +export function importBookmarks( + url: string, + account: AccountWithToken, + bookmarks: BookmarkInput[], +) { + return ofetch(`/users/${account.userId}/bookmarks`, { + method: "PUT", + body: bookmarks, + baseURL: url, + headers: getAuthHeaders(account.token), + }); +} diff --git a/src/backend/accounts/login.ts b/src/backend/accounts/login.ts new file mode 100644 index 00000000..13c80269 --- /dev/null +++ b/src/backend/accounts/login.ts @@ -0,0 +1,48 @@ +import { ofetch } from "ofetch"; + +import { SessionResponse } from "@/backend/accounts/auth"; + +export interface ChallengeTokenResponse { + challenge: string; +} + +export async function getLoginChallengeToken( + url: string, + publicKey: string, +): Promise { + return ofetch("/auth/login/start", { + method: "POST", + body: { + publicKey, + }, + baseURL: url, + }); +} + +export interface LoginResponse { + session: SessionResponse; + token: string; +} + +export interface LoginInput { + publicKey: string; + challenge: { + code: string; + signature: string; + }; + device: string; +} + +export async function loginAccount( + url: string, + data: LoginInput, +): Promise { + return ofetch("/auth/login/complete", { + method: "POST", + body: { + namespace: "movie-web", + ...data, + }, + baseURL: url, + }); +} diff --git a/src/backend/accounts/meta.ts b/src/backend/accounts/meta.ts new file mode 100644 index 00000000..0886dc11 --- /dev/null +++ b/src/backend/accounts/meta.ts @@ -0,0 +1,15 @@ +import { ofetch } from "ofetch"; + +export interface MetaResponse { + version: string; + name: string; + description?: string; + hasCaptcha: boolean; + captchaClientKey?: string; +} + +export async function getBackendMeta(url: string): Promise { + return ofetch("/meta", { + baseURL: url, + }); +} diff --git a/src/backend/accounts/progress.ts b/src/backend/accounts/progress.ts new file mode 100644 index 00000000..511d1c5d --- /dev/null +++ b/src/backend/accounts/progress.ts @@ -0,0 +1,115 @@ +import { ofetch } from "ofetch"; + +import { getAuthHeaders } from "@/backend/accounts/auth"; +import { ProgressResponse } from "@/backend/accounts/user"; +import { AccountWithToken } from "@/stores/auth"; +import { ProgressMediaItem, ProgressUpdateItem } from "@/stores/progress"; + +export interface ProgressInput { + meta?: { + title: string; + year: number; + poster?: string; + type: string; + }; + tmdbId: string; + watched: number; + duration: number; + seasonId?: string; + episodeId?: string; + seasonNumber?: number; + episodeNumber?: number; + updatedAt?: string; +} + +export function progressUpdateItemToInput( + item: ProgressUpdateItem, +): ProgressInput { + return { + duration: item.progress?.duration ?? 0, + watched: item.progress?.watched ?? 0, + tmdbId: item.tmdbId, + meta: { + title: item.title ?? "", + type: item.type ?? "", + year: item.year ?? NaN, + poster: item.poster, + }, + episodeId: item.episodeId, + seasonId: item.seasonId, + episodeNumber: item.episodeNumber, + seasonNumber: item.seasonNumber, + }; +} + +export function progressMediaItemToInputs( + tmdbId: string, + item: ProgressMediaItem, +): ProgressInput[] { + if (item.type === "show") { + return Object.entries(item.episodes).flatMap(([_, episode]) => ({ + duration: item.progress?.duration ?? episode.progress.duration, + watched: item.progress?.watched ?? episode.progress.watched, + tmdbId, + meta: { + title: item.title ?? "", + type: item.type ?? "", + year: item.year ?? NaN, + poster: item.poster, + }, + episodeId: episode.id, + seasonId: episode.seasonId, + episodeNumber: episode.number, + seasonNumber: item.seasons[episode.seasonId].number, + updatedAt: new Date(episode.updatedAt).toISOString(), + })); + } + return [ + { + duration: item.progress?.duration ?? 0, + watched: item.progress?.watched ?? 0, + tmdbId, + updatedAt: new Date(item.updatedAt).toISOString(), + meta: { + title: item.title ?? "", + type: item.type ?? "", + year: item.year ?? NaN, + poster: item.poster, + }, + }, + ]; +} + +export async function setProgress( + url: string, + account: AccountWithToken, + input: ProgressInput, +) { + return ofetch( + `/users/${account.userId}/progress/${input.tmdbId}`, + { + method: "PUT", + headers: getAuthHeaders(account.token), + baseURL: url, + body: input, + }, + ); +} + +export async function removeProgress( + url: string, + account: AccountWithToken, + id: string, + episodeId?: string, + seasonId?: string, +) { + await ofetch(`/users/${account.userId}/progress/${id}`, { + method: "DELETE", + headers: getAuthHeaders(account.token), + baseURL: url, + body: { + episodeId, + seasonId, + }, + }); +} diff --git a/src/backend/accounts/register.ts b/src/backend/accounts/register.ts new file mode 100644 index 00000000..8fd0f9e3 --- /dev/null +++ b/src/backend/accounts/register.ts @@ -0,0 +1,55 @@ +import { ofetch } from "ofetch"; + +import { SessionResponse } from "@/backend/accounts/auth"; +import { UserResponse } from "@/backend/accounts/user"; + +export interface ChallengeTokenResponse { + challenge: string; +} + +export async function getRegisterChallengeToken( + url: string, + captchaToken?: string, +): Promise { + return ofetch("/auth/register/start", { + method: "POST", + body: { + captchaToken, + }, + baseURL: url, + }); +} + +export interface RegisterResponse { + user: UserResponse; + session: SessionResponse; + token: string; +} + +export interface RegisterInput { + publicKey: string; + challenge: { + code: string; + signature: string; + }; + device: string; + profile: { + colorA: string; + colorB: string; + icon: string; + }; +} + +export async function registerAccount( + url: string, + data: RegisterInput, +): Promise { + return ofetch("/auth/register/complete", { + method: "POST", + body: { + namespace: "movie-web", + ...data, + }, + baseURL: url, + }); +} diff --git a/src/backend/accounts/sessions.ts b/src/backend/accounts/sessions.ts new file mode 100644 index 00000000..34c4af30 --- /dev/null +++ b/src/backend/accounts/sessions.ts @@ -0,0 +1,49 @@ +import { ofetch } from "ofetch"; + +import { getAuthHeaders } from "@/backend/accounts/auth"; +import { AccountWithToken } from "@/stores/auth"; + +export interface SessionResponse { + id: string; + userId: string; + createdAt: string; + accessedAt: string; + device: string; + userAgent: string; +} + +export interface SessionUpdate { + deviceName: string; +} + +export async function getSessions(url: string, account: AccountWithToken) { + return ofetch(`/users/${account.userId}/sessions`, { + headers: getAuthHeaders(account.token), + baseURL: url, + }); +} + +export async function updateSession( + url: string, + account: AccountWithToken, + update: SessionUpdate, +) { + return ofetch(`/sessions/${account.sessionId}`, { + method: "PATCH", + headers: getAuthHeaders(account.token), + body: update, + baseURL: url, + }); +} + +export async function removeSession( + url: string, + token: string, + sessionId: string, +) { + return ofetch(`/sessions/${sessionId}`, { + method: "DELETE", + headers: getAuthHeaders(token), + baseURL: url, + }); +} diff --git a/src/backend/accounts/settings.ts b/src/backend/accounts/settings.ts new file mode 100644 index 00000000..c749795b --- /dev/null +++ b/src/backend/accounts/settings.ts @@ -0,0 +1,39 @@ +import { ofetch } from "ofetch"; + +import { getAuthHeaders } from "@/backend/accounts/auth"; +import { AccountWithToken } from "@/stores/auth"; + +export interface SettingsInput { + applicationLanguage?: string; + applicationTheme?: string | null; + defaultSubtitleLanguage?: string; + proxyUrls?: string[] | null; +} + +export interface SettingsResponse { + applicationTheme?: string | null; + applicationLanguage?: string | null; + defaultSubtitleLanguage?: string | null; + proxyUrls?: string[] | null; +} + +export function updateSettings( + url: string, + account: AccountWithToken, + settings: SettingsInput, +) { + return ofetch(`/users/${account.userId}/settings`, { + method: "PUT", + body: settings, + baseURL: url, + headers: getAuthHeaders(account.token), + }); +} + +export function getSettings(url: string, account: AccountWithToken) { + return ofetch(`/users/${account.userId}/settings`, { + method: "GET", + baseURL: url, + headers: getAuthHeaders(account.token), + }); +} diff --git a/src/backend/accounts/user.ts b/src/backend/accounts/user.ts new file mode 100644 index 00000000..4708dd2b --- /dev/null +++ b/src/backend/accounts/user.ts @@ -0,0 +1,178 @@ +import { ofetch } from "ofetch"; + +import { SessionResponse, getAuthHeaders } from "@/backend/accounts/auth"; +import { AccountWithToken } from "@/stores/auth"; +import { BookmarkMediaItem } from "@/stores/bookmarks"; +import { ProgressMediaItem } from "@/stores/progress"; + +export interface UserResponse { + id: string; + namespace: string; + name: string; + roles: string[]; + createdAt: string; + profile: { + colorA: string; + colorB: string; + icon: string; + }; +} + +export interface UserEdit { + profile?: { + colorA: string; + colorB: string; + icon: string; + }; +} + +export interface BookmarkResponse { + tmdbId: string; + meta: { + title: string; + year: number; + poster?: string; + type: "show" | "movie"; + }; + updatedAt: string; +} + +export interface ProgressResponse { + tmdbId: string; + season: { + id?: string; + number?: number; + }; + episode: { + id?: string; + number?: number; + }; + meta: { + title: string; + year: number; + poster?: string; + type: "show" | "movie"; + }; + duration: string; + watched: string; + updatedAt: string; +} + +export function bookmarkResponsesToEntries(responses: BookmarkResponse[]) { + const entries = responses.map((bookmark) => { + const item: BookmarkMediaItem = { + ...bookmark.meta, + updatedAt: new Date(bookmark.updatedAt).getTime(), + }; + return [bookmark.tmdbId, item] as const; + }); + + return Object.fromEntries(entries); +} + +export function progressResponsesToEntries(responses: ProgressResponse[]) { + const items: Record = {}; + + responses.forEach((v) => { + if (!items[v.tmdbId]) { + items[v.tmdbId] = { + title: v.meta.title, + poster: v.meta.poster, + type: v.meta.type, + updatedAt: new Date(v.updatedAt).getTime(), + episodes: {}, + seasons: {}, + year: v.meta.year, + }; + } + + const item = items[v.tmdbId]; + + // Since each watched episode is a single array entry but with the same tmdbId, the root item updatedAt will only have the first episode's timestamp (which is not the newest). + // Here, we are setting it explicitly so the updatedAt always has the highest updatedAt from the episodes. + if (new Date(v.updatedAt).getTime() > item.updatedAt) { + item.updatedAt = new Date(v.updatedAt).getTime(); + } + + if (item.type === "movie") { + item.progress = { + duration: Number(v.duration), + watched: Number(v.watched), + }; + } + + if (item.type === "show" && v.season.id && v.episode.id) { + item.seasons[v.season.id] = { + id: v.season.id, + number: v.season.number ?? 0, + title: "", + }; + item.episodes[v.episode.id] = { + id: v.episode.id, + number: v.episode.number ?? 0, + title: "", + progress: { + duration: Number(v.duration), + watched: Number(v.watched), + }, + seasonId: v.season.id, + updatedAt: new Date(v.updatedAt).getTime(), + }; + } + }); + + return items; +} + +export async function getUser( + url: string, + token: string, +): Promise<{ user: UserResponse; session: SessionResponse }> { + return ofetch<{ user: UserResponse; session: SessionResponse }>( + "/users/@me", + { + headers: getAuthHeaders(token), + baseURL: url, + }, + ); +} + +export async function editUser( + url: string, + account: AccountWithToken, + object: UserEdit, +): Promise<{ user: UserResponse; session: SessionResponse }> { + return ofetch<{ user: UserResponse; session: SessionResponse }>( + `/users/${account.userId}`, + { + method: "PATCH", + headers: getAuthHeaders(account.token), + body: object, + baseURL: url, + }, + ); +} + +export async function deleteUser( + url: string, + account: AccountWithToken, +): Promise { + return ofetch(`/users/${account.userId}`, { + headers: getAuthHeaders(account.token), + baseURL: url, + }); +} + +export async function getBookmarks(url: string, account: AccountWithToken) { + return ofetch(`/users/${account.userId}/bookmarks`, { + headers: getAuthHeaders(account.token), + baseURL: url, + }); +} + +export async function getProgress(url: string, account: AccountWithToken) { + return ofetch(`/users/${account.userId}/progress`, { + headers: getAuthHeaders(account.token), + baseURL: url, + }); +} diff --git a/src/backend/extension/compatibility.ts b/src/backend/extension/compatibility.ts new file mode 100644 index 00000000..00188a04 --- /dev/null +++ b/src/backend/extension/compatibility.ts @@ -0,0 +1,7 @@ +import { satisfies } from "semver"; + +const allowedExtensionRange = "^1.0.2"; + +export function isAllowedExtensionVersion(version: string): boolean { + return satisfies(version, allowedExtensionRange); +} diff --git a/src/backend/extension/messaging.ts b/src/backend/extension/messaging.ts new file mode 100644 index 00000000..3204d541 --- /dev/null +++ b/src/backend/extension/messaging.ts @@ -0,0 +1,80 @@ +import { + MessagesMetadata, + sendToBackgroundViaRelay, +} from "@plasmohq/messaging"; + +import { isAllowedExtensionVersion } from "@/backend/extension/compatibility"; +import { ExtensionMakeRequestResponse } from "@/backend/extension/plasmo"; + +// for some reason, about 500 ms is needed after +// page load before the extension starts responding properly +const isExtensionReady = new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 500); +}); + +let activeExtension = false; + +async function sendMessage( + message: MessageKey, + payload: MessagesMetadata[MessageKey]["req"] | undefined = undefined, + timeout: number = -1, +) { + await isExtensionReady; + return new Promise((resolve) => { + if (timeout >= 0) setTimeout(() => resolve(null), timeout); + sendToBackgroundViaRelay< + MessagesMetadata[MessageKey]["req"], + MessagesMetadata[MessageKey]["res"] + >({ + name: message, + body: payload, + }) + .then((res) => { + activeExtension = true; + resolve(res); + }) + .catch(() => { + activeExtension = false; + resolve(null); + }); + }); +} + +export async function sendExtensionRequest( + ops: MessagesMetadata["makeRequest"]["req"], +): Promise | null> { + return sendMessage("makeRequest", ops); +} + +export async function setDomainRule( + ops: MessagesMetadata["prepareStream"]["req"], +): Promise { + return sendMessage("prepareStream", ops); +} + +export async function sendPage( + ops: MessagesMetadata["openPage"]["req"], +): Promise { + return sendMessage("openPage", ops); +} + +export async function extensionInfo(): Promise< + MessagesMetadata["hello"]["res"] | null +> { + const message = await sendMessage("hello", undefined, 500); + return message; +} + +export function isExtensionActiveCached(): boolean { + return activeExtension; +} + +export async function isExtensionActive(): Promise { + const info = await extensionInfo(); + if (!info?.success) return false; + const allowedVersion = isAllowedExtensionVersion(info.version); + if (!allowedVersion) return false; + return info.allowed && info.hasPermission; +} diff --git a/src/backend/extension/plasmo.ts b/src/backend/extension/plasmo.ts new file mode 100644 index 00000000..14a5481f --- /dev/null +++ b/src/backend/extension/plasmo.ts @@ -0,0 +1,71 @@ +export interface ExtensionBaseRequest {} + +export type ExtensionBaseResponse = + | ({ + success: true; + } & T) + | { + success: false; + error: string; + }; + +export type ExtensionHelloResponse = ExtensionBaseResponse<{ + version: string; + allowed: boolean; + hasPermission: boolean; +}>; + +export interface ExtensionMakeRequest extends ExtensionBaseRequest { + url: string; + method: string; + headers?: Record; + body?: string | Record; + bodyType?: "string" | "FormData" | "URLSearchParams" | "object"; +} + +export type ExtensionMakeRequestBodyType = ExtensionMakeRequest["bodyType"]; + +export type ExtensionMakeRequestResponse = ExtensionBaseResponse<{ + response: { + statusCode: number; + headers: Record; + finalUrl: string; + body: T; + }; +}>; + +export interface ExtensionPrepareStreamRequest extends ExtensionBaseRequest { + ruleId: number; + targetDomains: string[]; + requestHeaders?: Record; + responseHeaders?: Record; +} + +export interface MmMetadata { + hello: { + req: ExtensionBaseRequest; + res: ExtensionHelloResponse; + }; + makeRequest: { + req: ExtensionMakeRequest; + res: ExtensionMakeRequestResponse; + }; + prepareStream: { + req: ExtensionPrepareStreamRequest; + res: ExtensionBaseResponse; + }; + openPage: { + req: ExtensionBaseRequest & { + page: string; + redirectUrl: string; + }; + res: ExtensionBaseResponse; + }; +} + +interface MpMetadata {} + +declare module "@plasmohq/messaging" { + interface MessagesMetadata extends MmMetadata {} + interface PortsMetadata extends MpMetadata {} +} diff --git a/src/backend/extension/request.ts b/src/backend/extension/request.ts new file mode 100644 index 00000000..5885225e --- /dev/null +++ b/src/backend/extension/request.ts @@ -0,0 +1,17 @@ +import { ExtensionMakeRequestBodyType } from "./plasmo"; + +export function getBodyTypeFromBody( + body: unknown, +): ExtensionMakeRequestBodyType { + if (typeof body === "string") return "string"; + if (body instanceof FormData) return "FormData"; + if (body instanceof URLSearchParams) return "URLSearchParams"; + return "object"; +} + +export function convertBodyToObject(body: unknown): any { + if (body instanceof FormData || body instanceof URLSearchParams) { + return [...body]; + } + return body; +} diff --git a/src/backend/extension/streams.ts b/src/backend/extension/streams.ts new file mode 100644 index 00000000..588718af --- /dev/null +++ b/src/backend/extension/streams.ts @@ -0,0 +1,43 @@ +import { Stream } from "@movie-web/providers"; + +import { setDomainRule } from "@/backend/extension/messaging"; + +function extractDomain(url: string): string | null { + try { + const u = new URL(url); + return u.hostname; + } catch { + return null; + } +} + +function extractDomainsFromStream(stream: Stream): string[] { + if (stream.type === "hls") { + return [extractDomain(stream.playlist)].filter((v): v is string => !!v); + } + if (stream.type === "file") { + return Object.values(stream.qualities) + .map((v) => extractDomain(v.url)) + .filter((v): v is string => !!v); + } + return []; +} + +function buildHeadersFromStream(stream: Stream): Record { + const headers: Record = {}; + Object.entries(stream.headers ?? {}).forEach((entry) => { + headers[entry[0]] = entry[1]; + }); + Object.entries(stream.preferredHeaders ?? {}).forEach((entry) => { + headers[entry[0]] = entry[1]; + }); + return headers; +} + +export async function prepareStream(stream: Stream) { + await setDomainRule({ + ruleId: 1, + targetDomains: extractDomainsFromStream(stream), + requestHeaders: buildHeadersFromStream(stream), + }); +} diff --git a/src/backend/helpers/fetch.ts b/src/backend/helpers/fetch.ts new file mode 100644 index 00000000..f9aa145a --- /dev/null +++ b/src/backend/helpers/fetch.ts @@ -0,0 +1,79 @@ +import { ofetch } from "ofetch"; + +import { getApiToken, setApiToken } from "@/backend/helpers/providerApi"; +import { getLoadbalancedProxyUrl } from "@/backend/providers/fetchers"; + +type P = Parameters>; +type R = ReturnType>; + +const baseFetch = ofetch.create({ + retry: 0, +}); + +export function makeUrl(url: string, data: Record) { + let parsedUrl: string = url; + Object.entries(data).forEach(([k, v]) => { + parsedUrl = parsedUrl.replace(`{${k}}`, encodeURIComponent(v)); + }); + return parsedUrl; +} + +export function mwFetch(url: string, ops: P[1] = {}): R { + return baseFetch(url, ops); +} + +export async function singularProxiedFetch( + proxyUrl: string, + url: string, + ops: P[1] = {}, +): R { + let combinedUrl = ops?.baseURL ?? ""; + if ( + combinedUrl.length > 0 && + combinedUrl.endsWith("/") && + url.startsWith("/") + ) + combinedUrl += url.slice(1); + else if ( + combinedUrl.length > 0 && + !combinedUrl.endsWith("/") && + !url.startsWith("/") + ) + combinedUrl += `/${url}`; + else combinedUrl += url; + + const parsedUrl = new URL(combinedUrl); + Object.entries(ops?.params ?? {}).forEach(([k, v]) => { + parsedUrl.searchParams.set(k, v); + }); + Object.entries(ops?.query ?? {}).forEach(([k, v]) => { + parsedUrl.searchParams.set(k, v); + }); + + let headers = ops.headers ?? {}; + const apiToken = await getApiToken(); + if (apiToken) + headers = { + ...headers, + "X-Token": apiToken, + }; + + return baseFetch(proxyUrl, { + ...ops, + baseURL: undefined, + params: { + destination: parsedUrl.toString(), + }, + query: {}, + headers, + onResponse(context) { + const tokenHeader = context.response.headers.get("X-Token"); + if (tokenHeader) setApiToken(tokenHeader); + ops.onResponse?.(context); + }, + }); +} + +export function proxiedFetch(url: string, ops: P[1] = {}): R { + return singularProxiedFetch(getLoadbalancedProxyUrl(), url, ops); +} diff --git a/src/backend/helpers/providerApi.ts b/src/backend/helpers/providerApi.ts new file mode 100644 index 00000000..528e392c --- /dev/null +++ b/src/backend/helpers/providerApi.ts @@ -0,0 +1,163 @@ +import { MetaOutput, NotFoundError, ScrapeMedia } from "@movie-web/providers"; +import { jwtDecode } from "jwt-decode"; + +import { mwFetch } from "@/backend/helpers/fetch"; +import { getTurnstileToken, isTurnstileInitialized } from "@/stores/turnstile"; + +let metaDataCache: MetaOutput[] | null = null; +let token: null | string = null; + +export function setCachedMetadata(data: MetaOutput[]) { + metaDataCache = data; +} + +export function getCachedMetadata(): MetaOutput[] { + return metaDataCache ?? []; +} + +export function setApiToken(newToken: string) { + token = newToken; +} + +function getTokenIfValid(): null | string { + if (!token) return null; + try { + const body = jwtDecode(token); + if (!body.exp) return `jwt|${token}`; + if (Date.now() / 1000 < body.exp) return `jwt|${token}`; + } catch (err) { + // we dont care about parse errors + } + return null; +} + +export async function fetchMetadata(base: string) { + if (metaDataCache) return; + const data = await mwFetch(`${base}/metadata`); + metaDataCache = data.flat(); +} + +function scrapeMediaToQueryMedia(media: ScrapeMedia) { + let extra: Record = {}; + if (media.type === "show") { + extra = { + episodeNumber: media.episode.number.toString(), + episodeTmdbId: media.episode.tmdbId, + seasonNumber: media.season.number.toString(), + seasonTmdbId: media.season.tmdbId, + }; + } + + return { + type: media.type, + releaseYear: media.releaseYear.toString(), + imdbId: media.imdbId, + tmdbId: media.tmdbId, + title: media.title, + ...extra, + }; +} + +function addQueryDataToUrl(url: URL, data: Record) { + Object.entries(data).forEach((entry) => { + if (entry[1]) url.searchParams.set(entry[0], entry[1]); + }); +} + +export function makeProviderUrl(base: string) { + const makeUrl = (p: string) => new URL(`${base}${p}`); + return { + scrapeSource(sourceId: string, media: ScrapeMedia) { + const url = makeUrl("/scrape/source"); + addQueryDataToUrl(url, scrapeMediaToQueryMedia(media)); + addQueryDataToUrl(url, { id: sourceId }); + return url.toString(); + }, + scrapeAll(media: ScrapeMedia) { + const url = makeUrl("/scrape"); + addQueryDataToUrl(url, scrapeMediaToQueryMedia(media)); + return url.toString(); + }, + scrapeEmbed(embedId: string, embedUrl: string) { + const url = makeUrl("/scrape/embed"); + addQueryDataToUrl(url, { id: embedId, url: embedUrl }); + return url.toString(); + }, + }; +} + +export async function getApiToken(): Promise { + let apiToken = getTokenIfValid(); + if (!apiToken && isTurnstileInitialized()) { + apiToken = `turnstile|${await getTurnstileToken()}`; + } + return apiToken; +} + +function parseEventInput(inp: string): any { + if (inp.length === 0) return {}; + return JSON.parse(inp); +} + +export async function connectServerSideEvents( + url: string, + endEvents: string[], +) { + const apiToken = await getApiToken(); + + // insert token, if its set + const parsedUrl = new URL(url); + if (apiToken) parsedUrl.searchParams.set("token", apiToken); + const eventSource = new EventSource(parsedUrl.toString()); + + let promReject: (reason?: any) => void; + let promResolve: (value: T) => void; + const promise = new Promise((resolve, reject) => { + promResolve = resolve; + promReject = reject; + }); + + endEvents.forEach((evt) => { + eventSource.addEventListener(evt, (e) => { + eventSource.close(); + promResolve(parseEventInput(e.data)); + }); + }); + + eventSource.addEventListener("token", (e) => { + setApiToken(parseEventInput(e.data)); + }); + + eventSource.addEventListener("error", (err: MessageEvent) => { + eventSource.close(); + if (err.data) { + const data = JSON.parse(err.data); + let errObj = new Error("scrape error"); + if (data.name === NotFoundError.name) + errObj = new NotFoundError("Notfound from server"); + Object.assign(errObj, data); + promReject(errObj); + return; + } + + console.error("Failed to connect to SSE", err); + promReject(err); + }); + + eventSource.addEventListener("message", (ev) => { + if (!ev) { + eventSource.close(); + return; + } + setTimeout(() => { + promReject(new Error("SSE closed improperly")); + }, 1000); + }); + + return { + promise: () => promise, + on(event: string, cb: (data: Data) => void) { + eventSource.addEventListener(event, (e) => cb(JSON.parse(e.data))); + }, + }; +} diff --git a/src/backend/helpers/report.ts b/src/backend/helpers/report.ts new file mode 100644 index 00000000..4822a867 --- /dev/null +++ b/src/backend/helpers/report.ts @@ -0,0 +1,165 @@ +import { ScrapeMedia } from "@movie-web/providers"; +import { nanoid } from "nanoid"; +import { ofetch } from "ofetch"; +import { useCallback } from "react"; + +import { isExtensionActiveCached } from "@/backend/extension/messaging"; +import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape"; +import { useAuthStore } from "@/stores/auth"; +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 captchaMetricsEndpoint = "https://backend.movie-web.app/metrics/captcha"; +const batchId = () => nanoid(32); + +export type ProviderMetric = { + tmdbId: string; + type: string; + title: string; + seasonId?: string; + episodeId?: string; + status: "failed" | "notfound" | "success"; + providerId: string; + embedId?: string; + errorMessage?: string; + fullError?: string; +}; + +export type ScrapeTool = "default" | "custom-proxy" | "extension"; + +export function getScrapeTool(): ScrapeTool { + if (isExtensionActiveCached()) return "extension"; + const hasProxySet = !!useAuthStore.getState().proxySet; + if (hasProxySet) return "custom-proxy"; + return "default"; +} + +function getStackTrace(error: Error, lines: number) { + const topMessage = error.toString(); + const stackTraceLines = (error.stack ?? "").split("\n", lines + 1); + stackTraceLines.pop(); + return `${topMessage}\n\n${stackTraceLines.join("\n")}`; +} + +export async function reportProviders(items: ProviderMetric[]): Promise { + return ofetch(metricsEndpoint, { + method: "POST", + body: { + items, + tool: getScrapeTool(), + batchId: batchId(), + }, + }); +} + +const segmentStatusMap: Record< + ScrapingSegment["status"], + ProviderMetric["status"] | null +> = { + success: "success", + notfound: "notfound", + failure: "failed", + pending: null, + waiting: null, +}; + +export function scrapeSourceOutputToProviderMetric( + media: PlayerMeta, + providerId: string, + embedId: string | null, + status: ProviderMetric["status"], + err: unknown | null, +): ProviderMetric { + const episodeId = media.episode?.tmdbId; + const seasonId = media.season?.tmdbId; + let error: undefined | Error; + if (err instanceof Error) error = err; + + return { + status, + providerId, + title: media.title, + tmdbId: media.tmdbId, + type: media.type, + embedId: embedId ?? undefined, + episodeId, + seasonId, + errorMessage: error?.message, + fullError: error ? getStackTrace(error, 5) : undefined, + }; +} + +export function scrapeSegmentToProviderMetric( + media: ScrapeMedia, + providerId: string, + segment: ScrapingSegment, +): ProviderMetric | null { + const status = segmentStatusMap[segment.status]; + if (!status) return null; + let episodeId: string | undefined; + let seasonId: string | undefined; + if (media.type === "show") { + episodeId = media.episode.tmdbId; + seasonId = media.season.tmdbId; + } + let error: undefined | Error; + if (segment.error instanceof Error) error = segment.error; + + return { + status, + providerId, + title: media.title, + tmdbId: media.tmdbId, + type: media.type, + embedId: segment.embedId, + episodeId, + seasonId, + errorMessage: segment.reason ?? error?.message, + fullError: error ? getStackTrace(error, 5) : undefined, + }; +} + +export function scrapePartsToProviderMetric( + media: ScrapeMedia, + order: ScrapingItems[], + sources: Record, +): ProviderMetric[] { + const output: ProviderMetric[] = []; + + order.forEach((orderItem) => { + const source = sources[orderItem.id]; + orderItem.children.forEach((embedId) => { + const embed = sources[embedId]; + if (!embed.embedId) return; + const metric = scrapeSegmentToProviderMetric(media, source.id, embed); + if (!metric) return; + output.push(metric); + }); + + const metric = scrapeSegmentToProviderMetric(media, source.id, source); + if (!metric) return; + output.push(metric); + }); + + return output; +} + +export function useReportProviders() { + const report = useCallback((items: ProviderMetric[]) => { + if (items.length === 0) return; + reportProviders(items).catch(() => {}); + }, []); + + return { report }; +} + +export function reportCaptchaSolve(success: boolean) { + ofetch(captchaMetricsEndpoint, { + method: "POST", + body: { + success, + }, + }).catch(() => {}); +} diff --git a/src/backend/helpers/subs.ts b/src/backend/helpers/subs.ts new file mode 100644 index 00000000..455f05a9 --- /dev/null +++ b/src/backend/helpers/subs.ts @@ -0,0 +1,33 @@ +import { list } from "subsrt-ts"; + +import { proxiedFetch } from "@/backend/helpers/fetch"; +import { convertSubtitlesToSrt } from "@/components/player/utils/captions"; +import { CaptionListItem } from "@/stores/player/slices/source"; +import { SimpleCache } from "@/utils/cache"; + +export const subtitleTypeList = list().map((type) => `.${type}`); +const downloadCache = new SimpleCache(); +downloadCache.setCompare((a, b) => a === b); +const expirySeconds = 24 * 60 * 60; + +/** + * Always returns SRT + */ +export async function downloadCaption( + caption: CaptionListItem, +): Promise { + const cached = downloadCache.get(caption.url); + if (cached) return cached; + + let data: string | undefined; + if (caption.needsProxy) { + data = await proxiedFetch(caption.url, { responseType: "text" }); + } else { + data = await fetch(caption.url).then((v) => v.text()); + } + if (!data) throw new Error("failed to get caption data"); + + const output = convertSubtitlesToSrt(data); + downloadCache.set(caption.url, output, expirySeconds); + return output; +} diff --git a/src/backend/metadata/getmeta.ts b/src/backend/metadata/getmeta.ts new file mode 100644 index 00000000..add1089e --- /dev/null +++ b/src/backend/metadata/getmeta.ts @@ -0,0 +1,214 @@ +import { FetchError } from "ofetch"; + +import { formatJWMeta, mediaTypeToJW } from "./justwatch"; +import { + TMDBIdToUrlId, + TMDBMediaToMediaType, + formatTMDBMeta, + getEpisodes, + getMediaDetails, + getMediaPoster, + getMovieFromExternalId, + mediaTypeToTMDB, +} from "./tmdb"; +import { + JWDetailedMeta, + JWSeasonMetaResult, + JW_API_BASE, +} from "./types/justwatch"; +import { MWMediaMeta, MWMediaType } from "./types/mw"; +import { + TMDBContentTypes, + TMDBMediaResult, + TMDBMovieData, + TMDBSeasonMetaResult, + TMDBShowData, +} from "./types/tmdb"; +import { makeUrl, proxiedFetch } from "../helpers/fetch"; + +export interface DetailedMeta { + meta: MWMediaMeta; + imdbId?: string; + tmdbId?: string; +} + +export function formatTMDBMetaResult( + details: TMDBShowData | TMDBMovieData, + type: MWMediaType, +): TMDBMediaResult { + if (type === MWMediaType.MOVIE) { + const movie = details as TMDBMovieData; + return { + id: details.id, + title: movie.title, + object_type: mediaTypeToTMDB(type), + poster: getMediaPoster(movie.poster_path) ?? undefined, + original_release_year: new Date(movie.release_date).getFullYear(), + }; + } + if (type === MWMediaType.SERIES) { + const show = details as TMDBShowData; + return { + id: details.id, + title: show.name, + object_type: mediaTypeToTMDB(type), + seasons: show.seasons.map((v) => ({ + id: v.id, + season_number: v.season_number, + title: v.name, + })), + poster: getMediaPoster(show.poster_path) ?? undefined, + original_release_year: new Date(show.first_air_date).getFullYear(), + }; + } + + throw new Error("unsupported type"); +} + +export async function getMetaFromId( + type: MWMediaType, + id: string, + seasonId?: string, +): Promise { + const details = await getMediaDetails(id, mediaTypeToTMDB(type)); + + if (!details) return null; + + const imdbId = details.external_ids.imdb_id ?? undefined; + + let seasonData: TMDBSeasonMetaResult | undefined; + + if (type === MWMediaType.SERIES) { + const seasons = (details as TMDBShowData).seasons; + + let selectedSeason = seasons.find((v) => v.id.toString() === seasonId); + if (!selectedSeason) { + selectedSeason = seasons.find((v) => v.season_number === 1); + } + + if (selectedSeason) { + const episodes = await getEpisodes( + details.id.toString(), + selectedSeason.season_number, + ); + + seasonData = { + id: selectedSeason.id.toString(), + season_number: selectedSeason.season_number, + title: selectedSeason.name, + episodes, + }; + } + } + + const tmdbmeta = formatTMDBMetaResult(details, type); + if (!tmdbmeta) return null; + const meta = formatTMDBMeta(tmdbmeta, seasonData); + if (!meta) return null; + + return { + meta, + imdbId, + tmdbId: id, + }; +} + +export async function getLegacyMetaFromId( + type: MWMediaType, + id: string, + seasonId?: string, +): Promise { + const queryType = mediaTypeToJW(type); + + let data: JWDetailedMeta; + try { + const url = makeUrl("/content/titles/{type}/{id}/locale/en_US", { + type: queryType, + id, + }); + data = await proxiedFetch(url, { baseURL: JW_API_BASE }); + } catch (err) { + if (err instanceof FetchError) { + // 400 and 404 are treated as not found + if (err.statusCode === 400 || err.statusCode === 404) return null; + } + throw err; + } + + let imdbId = data.external_ids.find((v) => v.provider === "imdb_latest") + ?.external_id; + if (!imdbId) + imdbId = data.external_ids.find((v) => v.provider === "imdb")?.external_id; + + let tmdbId = data.external_ids.find((v) => v.provider === "tmdb_latest") + ?.external_id; + if (!tmdbId) + tmdbId = data.external_ids.find((v) => v.provider === "tmdb")?.external_id; + + let seasonData: JWSeasonMetaResult | undefined; + if (data.object_type === "show") { + const seasonToScrape = seasonId ?? data.seasons?.[0].id.toString() ?? ""; + const url = makeUrl("/content/titles/show_season/{id}/locale/en_US", { + id: seasonToScrape, + }); + seasonData = await proxiedFetch(url, { baseURL: JW_API_BASE }); + } + + return { + meta: formatJWMeta(data, seasonData), + imdbId, + tmdbId, + }; +} + +export function isLegacyUrl(url: string): boolean { + if (url.startsWith("/media/JW") || url.startsWith("/media/tmdb-show")) + return true; + return false; +} + +export function isLegacyMediaType(url: string): boolean { + if (url.startsWith("/media/tmdb-show")) return true; + return false; +} + +export async function convertLegacyUrl( + url: string, +): Promise { + if (!isLegacyUrl(url)) return undefined; + + const urlParts = url.split("/").slice(2); + const [, type, id] = urlParts[0].split("-", 3); + const suffix = urlParts + .slice(1) + .map((v) => `/${v}`) + .join(""); + + if (isLegacyMediaType(url)) { + const details = await getMediaDetails(id, TMDBContentTypes.TV); + return `/media/${TMDBIdToUrlId( + MWMediaType.SERIES, + details.id.toString(), + details.name, + )}${suffix}`; + } + + const mediaType = TMDBMediaToMediaType(type as TMDBContentTypes); + const meta = await getLegacyMetaFromId(mediaType, id); + + if (!meta) return undefined; + const { tmdbId, imdbId } = meta; + if (!tmdbId && !imdbId) return undefined; + + // movies always have an imdb id on tmdb + if (imdbId && mediaType === MWMediaType.MOVIE) { + const movieId = await getMovieFromExternalId(imdbId); + if (movieId) { + return `/media/${TMDBIdToUrlId(mediaType, movieId, meta.meta.title)}`; + } + + if (tmdbId) { + return `/media/${TMDBIdToUrlId(mediaType, tmdbId, meta.meta.title)}`; + } + } +} diff --git a/src/backend/metadata/justwatch.ts b/src/backend/metadata/justwatch.ts new file mode 100644 index 00000000..f4c79063 --- /dev/null +++ b/src/backend/metadata/justwatch.ts @@ -0,0 +1,84 @@ +import { + JWContentTypes, + JWMediaResult, + JWSeasonMetaResult, + JW_IMAGE_BASE, +} from "./types/justwatch"; +import { MWMediaMeta, MWMediaType, MWSeasonMeta } from "./types/mw"; + +export function mediaTypeToJW(type: MWMediaType): JWContentTypes { + if (type === MWMediaType.MOVIE) return "movie"; + if (type === MWMediaType.SERIES) return "show"; + throw new Error("unsupported type"); +} + +export function JWMediaToMediaType(type: string): MWMediaType { + if (type === "movie") return MWMediaType.MOVIE; + if (type === "show") return MWMediaType.SERIES; + throw new Error("unsupported type"); +} + +export function formatJWMeta( + media: JWMediaResult, + season?: JWSeasonMetaResult, +): MWMediaMeta { + const type = JWMediaToMediaType(media.object_type); + let seasons: undefined | MWSeasonMeta[]; + if (type === MWMediaType.SERIES) { + seasons = media.seasons + ?.sort((a, b) => a.season_number - b.season_number) + .map( + (v): MWSeasonMeta => ({ + id: v.id.toString(), + number: v.season_number, + title: v.title, + }), + ); + } + + return { + title: media.title, + id: media.id.toString(), + year: media.original_release_year?.toString(), + poster: media.poster + ? `${JW_IMAGE_BASE}${media.poster.replace("{profile}", "s166")}` + : undefined, + type, + seasons: seasons as any, + seasonData: season + ? ({ + id: season.id.toString(), + number: season.season_number, + title: season.title, + episodes: season.episodes + .sort((a, b) => a.episode_number - b.episode_number) + .map((v) => ({ + id: v.id.toString(), + number: v.episode_number, + title: v.title, + })), + } as any) + : (undefined as any), + }; +} + +export function JWMediaToId(media: MWMediaMeta): string { + return ["JW", mediaTypeToJW(media.type), media.id].join("-"); +} + +export function decodeJWId( + paramId: string, +): { id: string; type: MWMediaType } | null { + const [prefix, type, id] = paramId.split("-", 3); + if (prefix !== "JW") return null; + let mediaType; + try { + mediaType = JWMediaToMediaType(type); + } catch { + return null; + } + return { + type: mediaType, + id, + }; +} diff --git a/src/backend/metadata/search.ts b/src/backend/metadata/search.ts new file mode 100644 index 00000000..fff64112 --- /dev/null +++ b/src/backend/metadata/search.ts @@ -0,0 +1,35 @@ +import { SimpleCache } from "@/utils/cache"; +import { MediaItem } from "@/utils/mediaTypes"; + +import { + formatTMDBMetaToMediaItem, + formatTMDBSearchResult, + multiSearch, +} from "./tmdb"; +import { MWQuery } from "./types/mw"; + +const cache = new SimpleCache(); +cache.setCompare((a, b) => { + return a.searchQuery.trim() === b.searchQuery.trim(); +}); +cache.initialize(); + +export async function searchForMedia(query: MWQuery): Promise { + if (cache.has(query)) return cache.get(query) as MediaItem[]; + const { searchQuery } = query; + + const data = await multiSearch(searchQuery); + const results = data.map((v) => { + const formattedResult = formatTMDBSearchResult(v, v.media_type); + return formatTMDBMetaToMediaItem(formattedResult); + }); + + const movieWithPosters = results.filter((movie) => movie.poster); + const movieWithoutPosters = results.filter((movie) => !movie.poster); + + const sortedresult = movieWithPosters.concat(movieWithoutPosters); + + // cache results for 1 hour + cache.set(query, sortedresult, 3600); + return sortedresult; +} diff --git a/src/backend/metadata/tmdb.ts b/src/backend/metadata/tmdb.ts new file mode 100644 index 00000000..2bae75d1 --- /dev/null +++ b/src/backend/metadata/tmdb.ts @@ -0,0 +1,273 @@ +import slugify from "slugify"; + +import { conf } from "@/setup/config"; +import { MediaItem } from "@/utils/mediaTypes"; + +import { MWMediaMeta, MWMediaType, MWSeasonMeta } from "./types/mw"; +import { + ExternalIdMovieSearchResult, + TMDBContentTypes, + TMDBEpisodeShort, + TMDBMediaResult, + TMDBMovieData, + TMDBMovieSearchResult, + TMDBSearchResult, + TMDBSeason, + TMDBSeasonMetaResult, + TMDBShowData, + TMDBShowSearchResult, +} from "./types/tmdb"; +import { mwFetch } from "../helpers/fetch"; + +export function mediaTypeToTMDB(type: MWMediaType): TMDBContentTypes { + if (type === MWMediaType.MOVIE) return TMDBContentTypes.MOVIE; + if (type === MWMediaType.SERIES) return TMDBContentTypes.TV; + throw new Error("unsupported type"); +} + +export function mediaItemTypeToMediaType(type: MediaItem["type"]): MWMediaType { + if (type === "movie") return MWMediaType.MOVIE; + if (type === "show") return MWMediaType.SERIES; + throw new Error("unsupported type"); +} + +export function TMDBMediaToMediaType(type: TMDBContentTypes): MWMediaType { + if (type === TMDBContentTypes.MOVIE) return MWMediaType.MOVIE; + if (type === TMDBContentTypes.TV) return MWMediaType.SERIES; + throw new Error("unsupported type"); +} + +export function TMDBMediaToMediaItemType( + type: TMDBContentTypes, +): MediaItem["type"] { + if (type === TMDBContentTypes.MOVIE) return "movie"; + if (type === TMDBContentTypes.TV) return "show"; + throw new Error("unsupported type"); +} + +export function formatTMDBMeta( + media: TMDBMediaResult, + season?: TMDBSeasonMetaResult, +): MWMediaMeta { + const type = TMDBMediaToMediaType(media.object_type); + let seasons: undefined | MWSeasonMeta[]; + if (type === MWMediaType.SERIES) { + seasons = media.seasons + ?.sort((a, b) => a.season_number - b.season_number) + .map( + (v): MWSeasonMeta => ({ + title: v.title, + id: v.id.toString(), + number: v.season_number, + }), + ); + } + + return { + title: media.title, + id: media.id.toString(), + year: media.original_release_year?.toString(), + poster: media.poster, + type, + seasons: seasons as any, + seasonData: season + ? { + id: season.id.toString(), + number: season.season_number, + title: season.title, + episodes: season.episodes + .sort((a, b) => a.episode_number - b.episode_number) + .map((v) => ({ + id: v.id.toString(), + number: v.episode_number, + title: v.title, + air_date: v.air_date, + })), + } + : (undefined as any), + }; +} + +export function formatTMDBMetaToMediaItem(media: TMDBMediaResult): MediaItem { + const type = TMDBMediaToMediaItemType(media.object_type); + + return { + title: media.title, + id: media.id.toString(), + year: media.original_release_year ?? 0, + poster: media.poster, + type, + }; +} + +export function TMDBIdToUrlId( + type: MWMediaType, + tmdbId: string, + title: string, +) { + return [ + "tmdb", + mediaTypeToTMDB(type), + tmdbId, + slugify(title, { lower: true, strict: true }), + ].join("-"); +} + +export function TMDBMediaToId(media: MWMediaMeta): string { + return TMDBIdToUrlId(media.type, media.id, media.title); +} + +export function mediaItemToId(media: MediaItem): string { + return TMDBIdToUrlId( + mediaItemTypeToMediaType(media.type), + media.id, + media.title, + ); +} + +export function decodeTMDBId( + paramId: string, +): { id: string; type: MWMediaType } | null { + const [prefix, type, id] = paramId.split("-", 3); + if (prefix !== "tmdb") return null; + let mediaType; + try { + mediaType = TMDBMediaToMediaType(type as TMDBContentTypes); + } catch { + return null; + } + return { + type: mediaType, + id, + }; +} + +const baseURL = "https://api.themoviedb.org/3"; + +const headers = { + accept: "application/json", + Authorization: `Bearer ${conf().TMDB_READ_API_KEY}`, +}; + +async function get(url: string, params?: object): Promise { + const res = await mwFetch(encodeURI(url), { + headers, + baseURL, + params: { + ...params, + }, + }); + return res; +} + +export async function multiSearch( + query: string, +): Promise<(TMDBMovieSearchResult | TMDBShowSearchResult)[]> { + const data = await get("search/multi", { + query, + include_adult: false, + language: "en-US", + page: 1, + }); + // filter out results that aren't movies or shows + const results = data.results.filter( + (r) => + r.media_type === TMDBContentTypes.MOVIE || + r.media_type === TMDBContentTypes.TV, + ); + return results; +} + +export async function generateQuickSearchMediaUrl( + query: string, +): Promise { + const data = await multiSearch(query); + if (data.length === 0) return undefined; + const result = data[0]; + const title = + result.media_type === TMDBContentTypes.MOVIE ? result.title : result.name; + + return `/media/${TMDBIdToUrlId( + TMDBMediaToMediaType(result.media_type), + result.id.toString(), + title, + )}`; +} + +// Conditional type which for inferring the return type based on the content type +type MediaDetailReturn = + T extends TMDBContentTypes.MOVIE + ? TMDBMovieData + : T extends TMDBContentTypes.TV + ? TMDBShowData + : never; + +export function getMediaDetails< + T extends TMDBContentTypes, + TReturn = MediaDetailReturn, +>(id: string, type: T): Promise { + if (type === TMDBContentTypes.MOVIE) { + return get(`/movie/${id}`, { append_to_response: "external_ids" }); + } + if (type === TMDBContentTypes.TV) { + return get(`/tv/${id}`, { append_to_response: "external_ids" }); + } + throw new Error("Invalid media type"); +} + +export function getMediaPoster(posterPath: string | null): string | undefined { + if (posterPath) return `https://image.tmdb.org/t/p/w342/${posterPath}`; +} + +export async function getEpisodes( + id: string, + season: number, +): Promise { + const data = await get(`/tv/${id}/season/${season}`); + return data.episodes.map((e) => ({ + id: e.id, + episode_number: e.episode_number, + title: e.name, + air_date: e.air_date, + })); +} + +export async function getMovieFromExternalId( + imdbId: string, +): Promise { + const data = await get(`/find/${imdbId}`, { + external_source: "imdb_id", + }); + + const movie = data.movie_results[0]; + if (!movie) return undefined; + + return movie.id.toString(); +} + +export function formatTMDBSearchResult( + result: TMDBMovieSearchResult | TMDBShowSearchResult, + mediatype: TMDBContentTypes, +): TMDBMediaResult { + const type = TMDBMediaToMediaType(mediatype); + if (type === MWMediaType.SERIES) { + const show = result as TMDBShowSearchResult; + return { + title: show.name, + poster: getMediaPoster(show.poster_path), + id: show.id, + original_release_year: new Date(show.first_air_date).getFullYear(), + object_type: mediatype, + }; + } + + const movie = result as TMDBMovieSearchResult; + + return { + title: movie.title, + poster: getMediaPoster(movie.poster_path), + id: movie.id, + original_release_year: new Date(movie.release_date).getFullYear(), + object_type: mediatype, + }; +} diff --git a/src/backend/metadata/types/justwatch.ts b/src/backend/metadata/types/justwatch.ts new file mode 100644 index 00000000..b55e9e24 --- /dev/null +++ b/src/backend/metadata/types/justwatch.ts @@ -0,0 +1,65 @@ +export type JWContentTypes = "movie" | "show"; + +export type JWSearchQuery = { + content_types: JWContentTypes[]; + page: number; + page_size: number; + query: string; +}; + +export type JWPage = { + items: T[]; + page: number; + page_size: number; + total_pages: number; + total_results: number; +}; + +export const JW_API_BASE = "https://apis.justwatch.com"; +export const JW_IMAGE_BASE = "https://images.justwatch.com"; + +export type JWSeasonShort = { + title: string; + id: number; + season_number: number; +}; + +export type JWEpisodeShort = { + title: string; + id: number; + episode_number: number; +}; + +export type JWMediaResult = { + title: string; + poster?: string; + id: number; + original_release_year?: number; + jw_entity_id: string; + object_type: JWContentTypes; + seasons?: JWSeasonShort[]; +}; + +export type JWSeasonMetaResult = { + title: string; + id: string; + season_number: number; + episodes: JWEpisodeShort[]; +}; + +export type JWExternalIdType = + | "eidr" + | "imdb_latest" + | "imdb" + | "tmdb_latest" + | "tmdb" + | "tms"; + +export interface JWExternalId { + provider: JWExternalIdType; + external_id: string; +} + +export interface JWDetailedMeta extends JWMediaResult { + external_ids: JWExternalId[]; +} diff --git a/src/backend/metadata/types/mw.ts b/src/backend/metadata/types/mw.ts new file mode 100644 index 00000000..7589c834 --- /dev/null +++ b/src/backend/metadata/types/mw.ts @@ -0,0 +1,53 @@ +export enum MWMediaType { + MOVIE = "movie", + SERIES = "series", + ANIME = "anime", +} + +export type MWSeasonMeta = { + id: string; + number: number; + title: string; +}; + +export type MWSeasonWithEpisodeMeta = { + id: string; + number: number; + title: string; + episodes: { + id: string; + number: number; + title: string; + air_date: string; + }[]; +}; + +type MWMediaMetaBase = { + title: string; + id: string; + year?: string; + poster?: string; +}; + +type MWMediaMetaSpecific = + | { + type: MWMediaType.MOVIE | MWMediaType.ANIME; + seasons: undefined; + } + | { + type: MWMediaType.SERIES; + seasons: MWSeasonMeta[]; + seasonData: MWSeasonWithEpisodeMeta; + }; + +export type MWMediaMeta = MWMediaMetaBase & MWMediaMetaSpecific; + +export interface MWQuery { + searchQuery: string; +} + +export interface DetailedMeta { + meta: MWMediaMeta; + imdbId?: string; + tmdbId?: string; +} diff --git a/src/backend/metadata/types/tmdb.ts b/src/backend/metadata/types/tmdb.ts new file mode 100644 index 00000000..1071d96c --- /dev/null +++ b/src/backend/metadata/types/tmdb.ts @@ -0,0 +1,289 @@ +export enum TMDBContentTypes { + MOVIE = "movie", + TV = "tv", +} + +export type TMDBSeasonShort = { + title: string; + id: number; + season_number: number; +}; + +export type TMDBEpisodeShort = { + title: string; + id: number; + episode_number: number; + air_date: string; +}; + +export type TMDBMediaResult = { + title: string; + poster?: string; + id: number; + original_release_year?: number; + object_type: TMDBContentTypes; + seasons?: TMDBSeasonShort[]; +}; + +export type TMDBSeasonMetaResult = { + title: string; + id: string; + season_number: number; + episodes: TMDBEpisodeShort[]; +}; + +export interface TMDBShowData { + adult: boolean; + backdrop_path: string | null; + created_by: { + id: number; + credit_id: string; + name: string; + gender: number; + profile_path: string | null; + }[]; + episode_run_time: number[]; + first_air_date: string; + genres: { + id: number; + name: string; + }[]; + homepage: string; + id: number; + in_production: boolean; + languages: string[]; + last_air_date: string; + last_episode_to_air: { + id: number; + name: string; + overview: string; + vote_average: number; + vote_count: number; + air_date: string; + episode_number: number; + production_code: string; + runtime: number | null; + season_number: number; + show_id: number; + still_path: string | null; + } | null; + name: string; + next_episode_to_air: { + id: number; + name: string; + overview: string; + vote_average: number; + vote_count: number; + air_date: string; + episode_number: number; + production_code: string; + runtime: number | null; + season_number: number; + show_id: number; + still_path: string | null; + } | null; + networks: { + id: number; + logo_path: string; + name: string; + origin_country: string; + }[]; + number_of_episodes: number; + number_of_seasons: number; + origin_country: string[]; + original_language: string; + original_name: string; + overview: string; + popularity: number; + poster_path: string | null; + production_companies: { + id: number; + logo_path: string | null; + name: string; + origin_country: string; + }[]; + production_countries: { + iso_3166_1: string; + name: string; + }[]; + seasons: { + air_date: string; + episode_count: number; + id: number; + name: string; + overview: string; + poster_path: string | null; + season_number: number; + }[]; + spoken_languages: { + english_name: string; + iso_639_1: string; + name: string; + }[]; + status: string; + tagline: string; + type: string; + vote_average: number; + vote_count: number; + external_ids: { + imdb_id: string | null; + }; +} + +export interface TMDBMovieData { + adult: boolean; + backdrop_path: string | null; + belongs_to_collection: { + id: number; + name: string; + poster_path: string | null; + backdrop_path: string | null; + } | null; + budget: number; + genres: { + id: number; + name: string; + }[]; + homepage: string | null; + id: number; + imdb_id: string | null; + original_language: string; + original_title: string; + overview: string | null; + popularity: number; + poster_path: string | null; + production_companies: { + id: number; + logo_path: string | null; + name: string; + origin_country: string; + }[]; + production_countries: { + iso_3166_1: string; + name: string; + }[]; + release_date: string; + revenue: number; + runtime: number | null; + spoken_languages: { + english_name: string; + iso_639_1: string; + name: string; + }[]; + status: string; + tagline: string | null; + title: string; + video: boolean; + vote_average: number; + vote_count: number; + external_ids: { + imdb_id: string | null; + }; +} + +export interface TMDBEpisodeResult { + season: number; + number: number; + title: string; + ids: { + trakt: number; + tvdb: number; + imdb: string; + tmdb: number; + }; +} + +export interface TMDBEpisode { + air_date: string; + episode_number: number; + id: number; + name: string; + overview: string; + production_code: string; + runtime: number; + season_number: number; + show_id: number; + still_path: string | null; + vote_average: number; + vote_count: number; + crew: any[]; + guest_stars: any[]; +} + +export interface TMDBSeason { + _id: string; + air_date: string; + episodes: TMDBEpisode[]; + name: string; + overview: string; + id: number; + poster_path: string | null; + season_number: number; +} + +export interface ExternalIdMovieSearchResult { + movie_results: { + adult: boolean; + backdrop_path: string; + id: number; + title: string; + original_language: string; + original_title: string; + overview: string; + poster_path: string; + media_type: string; + genre_ids: number[]; + popularity: number; + release_date: string; + video: boolean; + vote_average: number; + vote_count: number; + }[]; + person_results: any[]; + tv_results: any[]; + tv_episode_results: any[]; + tv_season_results: any[]; +} + +export interface TMDBMovieSearchResult { + adult: boolean; + backdrop_path: string; + id: number; + title: string; + original_language: string; + original_title: string; + overview: string; + poster_path: string; + media_type: TMDBContentTypes.MOVIE; + genre_ids: number[]; + popularity: number; + release_date: string; + video: boolean; + vote_average: number; + vote_count: number; +} + +export interface TMDBShowSearchResult { + adult: boolean; + backdrop_path: string; + id: number; + name: string; + original_language: string; + original_name: string; + overview: string; + poster_path: string; + media_type: TMDBContentTypes.TV; + genre_ids: number[]; + popularity: number; + first_air_date: string; + vote_average: number; + vote_count: number; + origin_country: string[]; +} + +export interface TMDBSearchResult { + page: number; + results: (TMDBMovieSearchResult | TMDBShowSearchResult)[]; + total_pages: number; + total_results: number; +} diff --git a/src/backend/providers/fetchers.ts b/src/backend/providers/fetchers.ts new file mode 100644 index 00000000..5cb6f6ba --- /dev/null +++ b/src/backend/providers/fetchers.ts @@ -0,0 +1,88 @@ +import { Fetcher, makeSimpleProxyFetcher } from "@movie-web/providers"; + +import { sendExtensionRequest } from "@/backend/extension/messaging"; +import { getApiToken, setApiToken } from "@/backend/helpers/providerApi"; +import { getProviderApiUrls, getProxyUrls } from "@/utils/proxyUrls"; + +import { convertBodyToObject, getBodyTypeFromBody } from "../extension/request"; + +function makeLoadbalancedList(getter: () => string[]) { + let listIndex = -1; + return () => { + const fetchers = getter(); + if (listIndex === -1 || listIndex >= fetchers.length) { + listIndex = Math.floor(Math.random() * fetchers.length); + } + const proxyUrl = fetchers[listIndex]; + listIndex = (listIndex + 1) % fetchers.length; + return proxyUrl; + }; +} + +export const getLoadbalancedProxyUrl = makeLoadbalancedList(getProxyUrls); +export const getLoadbalancedProviderApiUrl = + makeLoadbalancedList(getProviderApiUrls); + +async function fetchButWithApiTokens( + input: RequestInfo | URL, + init?: RequestInit | undefined, +): Promise { + const apiToken = await getApiToken(); + const headers = new Headers(init?.headers); + if (apiToken) headers.set("X-Token", apiToken); + const response = await fetch( + input, + init + ? { + ...init, + headers, + } + : undefined, + ); + const newApiToken = response.headers.get("X-Token"); + if (newApiToken) setApiToken(newApiToken); + return response; +} + +export function makeLoadBalancedSimpleProxyFetcher() { + const fetcher: Fetcher = async (a, b) => { + const currentFetcher = makeSimpleProxyFetcher( + getLoadbalancedProxyUrl(), + fetchButWithApiTokens, + ); + return currentFetcher(a, b); + }; + return fetcher; +} + +function makeFinalHeaders( + readHeaders: string[], + headers: Record, +): Headers { + const lowercasedHeaders = readHeaders.map((v) => v.toLowerCase()); + return new Headers( + Object.entries(headers).filter((entry) => + lowercasedHeaders.includes(entry[0].toLowerCase()), + ), + ); +} + +export function makeExtensionFetcher() { + const fetcher: Fetcher = async (url, ops) => { + const result = await sendExtensionRequest({ + url, + ...ops, + body: convertBodyToObject(ops.body), + bodyType: getBodyTypeFromBody(ops.body), + }); + if (!result?.success) throw new Error(`extension error: ${result?.error}`); + const res = result.response; + return { + body: res.body, + finalUrl: res.finalUrl, + statusCode: res.statusCode, + headers: makeFinalHeaders(ops.readHeaders, res.headers), + }; + }; + return fetcher; +} diff --git a/src/backend/providers/providers.ts b/src/backend/providers/providers.ts new file mode 100644 index 00000000..ac4a7dfa --- /dev/null +++ b/src/backend/providers/providers.ts @@ -0,0 +1,27 @@ +import { + makeProviders, + makeStandardFetcher, + targets, +} from "@movie-web/providers"; + +import { isExtensionActiveCached } from "@/backend/extension/messaging"; +import { + makeExtensionFetcher, + makeLoadBalancedSimpleProxyFetcher, +} from "@/backend/providers/fetchers"; + +export function getProviders() { + if (isExtensionActiveCached()) { + return makeProviders({ + fetcher: makeExtensionFetcher(), + target: targets.BROWSER_EXTENSION, + consistentIpForRequests: true, + }); + } + + return makeProviders({ + fetcher: makeStandardFetcher(fetch), + proxiedFetcher: makeLoadBalancedSimpleProxyFetcher(), + target: targets.BROWSER, + }); +} diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx new file mode 100644 index 00000000..ec883def --- /dev/null +++ b/src/components/Avatar.tsx @@ -0,0 +1,95 @@ +import classNames from "classnames"; +import { useMemo } from "react"; + +import { base64ToBuffer, decryptData } from "@/backend/accounts/crypto"; +import { Icon, Icons } from "@/components/Icon"; +import { UserIcon } from "@/components/UserIcon"; +import { AccountProfile } from "@/pages/parts/auth/AccountCreatePart"; +import { useAuthStore } from "@/stores/auth"; + +export interface AvatarProps { + profile: AccountProfile["profile"]; + sizeClass?: string; + iconClass?: string; + bottom?: React.ReactNode; +} + +export function Avatar(props: AvatarProps) { + return ( +
+
+ +
+ {props.bottom ? ( +
+ {props.bottom} +
+ ) : null} +
+ ); +} + +export function UserAvatar(props: { + sizeClass?: string; + iconClass?: string; + bottom?: React.ReactNode; + withName?: boolean; +}) { + const auth = useAuthStore(); + + const bufferSeed = useMemo( + () => + auth.account && auth.account.seed + ? base64ToBuffer(auth.account.seed) + : null, + [auth], + ); + + if (!auth.account || auth.account === null) return null; + + const deviceName = bufferSeed + ? decryptData(auth.account.deviceName, bufferSeed) + : "..."; + + return ( + <> + + {props.withName && bufferSeed ? ( + + {deviceName.length >= 20 + ? `${deviceName.slice(0, 20 - 1)}…` + : deviceName} + + ) : null} + + ); +} + +export function NoUserAvatar(props: { iconClass?: string }) { + return ( +
+ +
+ ); +} diff --git a/src/components/FlagIcon.tsx b/src/components/FlagIcon.tsx new file mode 100644 index 00000000..5546aaad --- /dev/null +++ b/src/components/FlagIcon.tsx @@ -0,0 +1,60 @@ +import classNames from "classnames"; + +import { getCountryCodeForLocale } from "@/utils/language"; +import "flag-icons/css/flag-icons.min.css"; + +export interface FlagIconProps { + country?: string; + langCode?: string; +} + +export function FlagIcon(props: FlagIconProps) { + let countryCode: string | null = props.country ?? null; + if (props.langCode) countryCode = getCountryCodeForLocale(props.langCode); + + if (props.langCode === "tok") + return ( +
+ +
+ ); + + if (props.langCode === "pirate") + return ( +
+ +
+ ); + + if (props.langCode === "minion") + return ( +
+
+
+
+
+
+
+ ); + + // Galicia - Not a country (Is a region of Spain) so have to add the flag manually + if (props.langCode === "gl-ES") + return ( +
+ +
+ ); + + let backgroundClass = "bg-video-context-flagBg"; + if (countryCode === "np") backgroundClass = "bg-white"; + + return ( + + ); +} diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx new file mode 100644 index 00000000..ec5e26cb --- /dev/null +++ b/src/components/Icon.tsx @@ -0,0 +1,170 @@ +import classNames from "classnames"; +import { memo, useEffect, useRef } from "react"; + +export enum Icons { + SEARCH = "search", + BOOKMARK = "bookmark", + BOOKMARK_OUTLINE = "bookmark_outline", + CLOCK = "clock", + EYE = "eye", + EYE_SLASH = "eyeSlash", + ARROW_LEFT = "arrowLeft", + ARROW_RIGHT = "arrowRight", + CHEVRON_DOWN = "chevronDown", + CHEVRON_UP = "chevronUp", + CHEVRON_RIGHT = "chevronRight", + CHEVRON_LEFT = "chevronLeft", + CLAPPER_BOARD = "clapperBoard", + FILM = "film", + DRAGON = "dragon", + WARNING = "warning", + MOVIE_WEB = "movieWeb", + DISCORD = "discord", + GITHUB = "github", + PLAY = "play", + PAUSE = "pause", + EXPAND = "expand", + COMPRESS = "compress", + VOLUME = "volume", + VOLUME_X = "volume_x", + X = "x", + EDIT = "edit", + AIRPLAY = "airplay", + EPISODES = "episodes", + SKIP_FORWARD = "skip_forward", + SKIP_BACKWARD = "skip_backward", + FILE = "file", + CAPTIONS = "captions", + LINK = "link", + CASTING = "casting", + CIRCLE_EXCLAMATION = "circle_exclamation", + DOWNLOAD = "download", + GEAR = "gear", + WATCH_PARTY = "watch_party", + PICTURE_IN_PICTURE = "pictureInPicture", + CHECKMARK = "checkmark", + TACHOMETER = "tachometer", + MAIL = "mail", + CIRCLE_CHECK = "circle_check", + SKIP_EPISODE = "skip_episode", + MORE_VERTICAL = "more_vertical", + IOS_SHARE = "ios_share", + IOS_FILES = "ios_files", + WAND = "wand", + COPY = "copy", + USER = "user", + UP_DOWN_ARROW = "up_down_arrow", + RISING_STAR = "rising_star", + SETTINGS = "settings", + COINS = "coins", + LOGOUT = "logout", + MENU = "menu", + LOCK = "lock", + UNLOCK = "unlock", + DONATION = "donation", + CIRCLE_QUESTION = "circle_question", + BRUSH = "brush", +} + +export interface IconProps { + icon: Icons; + className?: string; +} + +const iconList: Record = { + search: ``, + bookmark: ``, + clock: ``, + eye: ``, + eyeSlash: ``, + arrowLeft: ``, + chevronDown: ``, + chevronUp: ``, + chevronRight: ``, + chevronLeft: ``, + clapperBoard: ``, + film: ``, + dragon: ``, + warning: ``, + arrowRight: ``, + movieWeb: ``, + discord: ``, + github: ``, + play: ``, + pause: ``, + expand: ``, + compress: ``, + volume: ``, + volume_x: ``, + x: ``, + edit: ``, + bookmark_outline: ``, + airplay: ``, + episodes: ``, + skip_forward: ``, + skip_backward: ``, + file: ``, + captions: ``, + link: ``, + circle_exclamation: ``, + casting: "", + download: ``, + gear: ``, + watch_party: ``, + pictureInPicture: ``, + checkmark: ``, + tachometer: ``, + mail: ``, + circle_check: ``, + skip_episode: ``, + more_vertical: ``, + ios_share: ``, + ios_files: ``, + wand: ``, + copy: ``, + user: ``, + up_down_arrow: ``, + rising_star: ``, + settings: ``, + coins: ``, + logout: ``, + menu: ``, + lock: ``, + unlock: ``, + donation: ``, + circle_question: ``, + brush: ``, +}; + +function ChromeCastButton() { + const ref = useRef(null); + + useEffect(() => { + const tag = document.createElement("google-cast-launcher"); + tag.setAttribute("id", "castbutton"); + ref.current?.appendChild(tag); + }, []); + + return
; +} + +export const Icon = memo((props: IconProps) => { + if (props.icon === Icons.CASTING) { + return ; + } + + 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 ( + + ); +}); diff --git a/src/components/LinksDropdown.tsx b/src/components/LinksDropdown.tsx new file mode 100644 index 00000000..c0da66ee --- /dev/null +++ b/src/components/LinksDropdown.tsx @@ -0,0 +1,173 @@ +import classNames from "classnames"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; + +import { base64ToBuffer, decryptData } from "@/backend/accounts/crypto"; +import { UserAvatar } from "@/components/Avatar"; +import { Icon, Icons } from "@/components/Icon"; +import { Transition } from "@/components/utils/Transition"; +import { useAuth } from "@/hooks/auth/useAuth"; +import { conf } from "@/setup/config"; +import { useAuthStore } from "@/stores/auth"; + +function Divider() { + return
; +} + +function GoToLink(props: { + children: React.ReactNode; + href?: string; + className?: string; + onClick?: () => void; +}) { + const navigate = useNavigate(); + + const goTo = (href: string) => { + if (href.startsWith("http")) window.open(href, "_blank"); + else navigate(href); + }; + + return ( + { + evt.preventDefault(); + if (props.href) goTo(props.href); + else props.onClick?.(); + }} + className={props.className} + > + {props.children} + + ); +} + +function DropdownLink(props: { + children: React.ReactNode; + href?: string; + icon?: Icons; + highlight?: boolean; + className?: string; + onClick?: () => void; +}) { + return ( + + {props.icon ? : null} + {props.children} + + ); +} + +function CircleDropdownLink(props: { icon: Icons; href: string }) { + return ( + + + + ); +} + +export function LinksDropdown(props: { children: React.ReactNode }) { + const { t } = useTranslation(); + const [open, setOpen] = useState(false); + const deviceName = useAuthStore((s) => s.account?.deviceName); + const seed = useAuthStore((s) => s.account?.seed); + const bufferSeed = useMemo( + () => (seed ? base64ToBuffer(seed) : null), + [seed], + ); + const { logout } = useAuth(); + + useEffect(() => { + function onWindowClick(evt: MouseEvent) { + if ((evt.target as HTMLElement).closest(".is-dropdown")) return; + setOpen(false); + } + + window.addEventListener("click", onWindowClick); + return () => window.removeEventListener("click", onWindowClick); + }, []); + + const toggleOpen = useCallback(() => { + setOpen((s) => !s); + }, []); + + return ( +
+
evt.key === "Enter" && toggleOpen()} + > + {props.children} + +
+ +
+ {deviceName && bufferSeed ? ( + + + {decryptData(deviceName, bufferSeed)} + + ) : ( + + {t("navigation.menu.register")} + + )} + + + {t("navigation.menu.settings")} + + + {t("navigation.menu.about")} + + + {t("navigation.menu.donation")} + + {deviceName ? ( + + {t("navigation.menu.logout")} + + ) : null} + +
+ + + +
+
+
+
+ ); +} diff --git a/src/components/UserIcon.tsx b/src/components/UserIcon.tsx new file mode 100644 index 00000000..a3d30421 --- /dev/null +++ b/src/components/UserIcon.tsx @@ -0,0 +1,35 @@ +import { memo } from "react"; + +import { Icon, Icons } from "@/components/Icon"; + +export enum UserIcons { + USER_GROUP = "userGroup", + COUCH = "couch", + MOBILE = "mobile", + TICKET = "ticket", + HANDCUFFS = "handcuffs", +} + +export interface UserIconProps { + icon: UserIcons; + className?: string; +} + +const iconList: Record = { + userGroup: ``, + couch: ``, + mobile: ``, + ticket: ``, + handcuffs: ``, +}; + +export const UserIcon = memo((props: UserIconProps) => { + const icon = iconList[props.icon]; + if (!icon) return ; + return ( + + ); +}); diff --git a/src/components/buttons/Button.tsx b/src/components/buttons/Button.tsx new file mode 100644 index 00000000..1c1a2b61 --- /dev/null +++ b/src/components/buttons/Button.tsx @@ -0,0 +1,142 @@ +import classNames from "classnames"; +import { ReactNode, useCallback } from "react"; +import { useNavigate } from "react-router-dom"; + +import { Icon, Icons } from "@/components/Icon"; +import { Spinner } from "@/components/layout/Spinner"; + +interface Props { + icon?: Icons; + onClick?: ( + event: React.MouseEvent, + ) => void; + children?: ReactNode; + theme?: "white" | "purple" | "secondary" | "danger"; + padding?: string; + className?: string; + href?: string; + disabled?: boolean; + download?: string; + loading?: boolean; +} + +export function Button(props: Props) { + const navigate = useNavigate(); + const { onClick, href, loading } = props; + const cb = useCallback( + ( + event: React.MouseEvent< + HTMLAnchorElement | HTMLButtonElement, + MouseEvent + >, + ) => { + if (loading) return; + if (href && !onClick) navigate(href); + else onClick?.(event); + }, + [onClick, href, navigate, loading], + ); + + let colorClasses = "bg-white hover:bg-gray-200 text-black"; + if (props.theme === "purple") + colorClasses = "bg-buttons-purple hover:bg-buttons-purpleHover text-white"; + if (props.theme === "secondary") + colorClasses = + "bg-buttons-cancel hover:bg-buttons-cancelHover transition-colors duration-100 text-white"; + if (props.theme === "danger") + colorClasses = "bg-buttons-danger hover:bg-buttons-dangerHover text-white"; + + let classes = classNames( + "tabbable cursor-pointer inline-flex items-center justify-center rounded-lg font-medium transition-[transform,background-color] duration-100 active:scale-105 md:px-8", + props.padding ?? "px-4 py-3", + props.className, + colorClasses, + props.disabled ? "!cursor-not-allowed bg-opacity-60 text-opacity-60" : null, + ); + + if (props.disabled) + classes = classes + .split(" ") + .filter( + (className) => + !className.startsWith("hover:") && !className.startsWith("active:"), + ) + .join(" "); + + const content = ( + <> + {props.icon && !props.loading ? ( + + + + ) : null} + {props.loading ? ( + + + + ) : null} + {props.children} + + ); + + if ( + props.href && + (props.href.startsWith("https://") || props.href?.startsWith("data:")) + ) + return ( + + {content} + + ); + + if (props.href) + return ( + + {content} + + ); + + return ( + + ); +} + +// Sometimes you can't use normal button, due to not having access to a useHistory context +// When that happens, use this! +interface ButtonPlainProps { + onClick?: () => void; + children?: ReactNode; + theme?: "white" | "purple" | "secondary"; + className?: string; +} + +export function ButtonPlain(props: ButtonPlainProps) { + let colorClasses = "bg-white hover:bg-gray-200 text-black"; + if (props.theme === "purple") + colorClasses = "bg-buttons-purple hover:bg-buttons-purpleHover text-white"; + if (props.theme === "secondary") + colorClasses = + "bg-buttons-cancel hover:bg-buttons-cancelHover transition-colors duration-100 text-white"; + + const classes = classNames( + "cursor-pointer inline-flex items-center justify-center rounded-lg font-medium transition-[transform,background-color] duration-100 active:scale-105 md:px-8", + "px-4 py-3", + props.className, + colorClasses, + ); + + return ( + + ); +} diff --git a/src/components/buttons/EditButton.tsx b/src/components/buttons/EditButton.tsx new file mode 100644 index 00000000..793fb7c3 --- /dev/null +++ b/src/components/buttons/EditButton.tsx @@ -0,0 +1,37 @@ +import { useAutoAnimate } from "@formkit/auto-animate/react"; +import { useCallback } from "react"; +import { useTranslation } from "react-i18next"; + +import { Icon, Icons } from "@/components/Icon"; + +export interface EditButtonProps { + editing: boolean; + onEdit?: (editing: boolean) => void; +} + +export function EditButton(props: EditButtonProps) { + const { t } = useTranslation(); + const [parent] = useAutoAnimate(); + + const onClick = useCallback(() => { + props.onEdit?.(!props.editing); + }, [props]); + + return ( + + ); +} diff --git a/src/components/buttons/IconPatch.tsx b/src/components/buttons/IconPatch.tsx new file mode 100644 index 00000000..b9a7e525 --- /dev/null +++ b/src/components/buttons/IconPatch.tsx @@ -0,0 +1,34 @@ +import { Icon, Icons } from "@/components/Icon"; + +export interface IconPatchProps { + active?: boolean; + onClick?: () => void; + clickable?: boolean; + className?: string; + icon: Icons; + transparent?: boolean; + downsized?: boolean; +} + +export function IconPatch(props: IconPatchProps) { + const clickableClasses = props.clickable + ? "cursor-pointer hover:scale-110 hover:bg-pill-backgroundHover hover:text-white active:scale-125" + : ""; + const transparentClasses = props.transparent + ? "bg-opacity-0 hover:bg-opacity-50" + : ""; + const activeClasses = props.active + ? "bg-pill-backgroundHover text-white" + : ""; + const sizeClasses = props.downsized ? "h-10 w-10" : "h-12 w-12"; + + return ( +
+
+ +
+
+ ); +} diff --git a/src/components/buttons/Toggle.tsx b/src/components/buttons/Toggle.tsx new file mode 100644 index 00000000..8cedc245 --- /dev/null +++ b/src/components/buttons/Toggle.tsx @@ -0,0 +1,23 @@ +import classNames from "classnames"; + +export function Toggle(props: { onClick?: () => void; enabled?: boolean }) { + return ( + + ); +} diff --git a/src/components/form/ColorPicker.tsx b/src/components/form/ColorPicker.tsx new file mode 100644 index 00000000..ae323156 --- /dev/null +++ b/src/components/form/ColorPicker.tsx @@ -0,0 +1,40 @@ +import classNames from "classnames"; + +import { Icon, Icons } from "../Icon"; + +const colors = ["#0A54FF", "#CF2E68", "#F9DD7F", "#7652DD", "#2ECFA8"]; +export const initialColor = colors[0]; + +export function ColorPicker(props: { + label: string; + value: string; + onInput: (v: string) => void; +}) { + return ( +
+ {props.label ? ( +

{props.label}

+ ) : null} + +
+ {colors.map((color) => { + return ( + + ); + })} +
+
+ ); +} diff --git a/src/components/form/Dropdown.tsx b/src/components/form/Dropdown.tsx new file mode 100644 index 00000000..df647b5f --- /dev/null +++ b/src/components/form/Dropdown.tsx @@ -0,0 +1,68 @@ +import { Listbox, Transition } from "@headlessui/react"; +import React, { Fragment } from "react"; + +import { Icon, Icons } from "@/components/Icon"; + +export interface OptionItem { + id: string; + name: string; + leftIcon?: React.ReactNode; +} + +interface DropdownProps { + selectedItem: OptionItem; + setSelectedItem: (value: OptionItem) => void; + options: Array; +} + +export function Dropdown(props: DropdownProps) { + return ( +
+ + {() => ( + <> + + + {props.selectedItem.leftIcon + ? props.selectedItem.leftIcon + : null} + {props.selectedItem.name} + + + + + + + + {props.options.map((opt) => ( + + `cursor-pointer flex gap-4 items-center relative select-none py-3 pl-4 pr-4 ${ + active + ? "bg-background-secondaryHover text-type-link" + : "text-white" + }` + } + key={opt.id} + value={opt} + > + {opt.leftIcon ? opt.leftIcon : null} + {opt.name} + + ))} + + + + )} + +
+ ); +} diff --git a/src/components/form/IconPicker.tsx b/src/components/form/IconPicker.tsx new file mode 100644 index 00000000..fa8a8a3a --- /dev/null +++ b/src/components/form/IconPicker.tsx @@ -0,0 +1,47 @@ +import classNames from "classnames"; + +import { UserIcon, UserIcons } from "../UserIcon"; + +const icons = [ + UserIcons.USER_GROUP, + UserIcons.COUCH, + UserIcons.MOBILE, + UserIcons.TICKET, + UserIcons.HANDCUFFS, +]; +export const initialIcon = icons[0]; + +export function IconPicker(props: { + label: string; + value: UserIcons; + onInput: (v: UserIcons) => void; +}) { + return ( +
+ {props.label ? ( +

{props.label}

+ ) : null} + +
+ {icons.map((icon) => { + return ( + + ); + })} +
+
+ ); +} diff --git a/src/components/form/PassphraseDisplay.tsx b/src/components/form/PassphraseDisplay.tsx new file mode 100644 index 00000000..92918373 --- /dev/null +++ b/src/components/form/PassphraseDisplay.tsx @@ -0,0 +1,57 @@ +import { useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useCopyToClipboard, useMountedState } from "react-use"; + +import { Icon, Icons } from "../Icon"; + +export function PassphraseDisplay(props: { mnemonic: string }) { + const { t } = useTranslation(); + const individualWords = props.mnemonic.split(" "); + + const [, copy] = useCopyToClipboard(); + + const [hasCopied, setHasCopied] = useState(false); + const isMounted = useMountedState(); + + const timeout = useRef>(); + + function copyMnemonic() { + copy(props.mnemonic); + setHasCopied(true); + if (timeout.current) clearTimeout(timeout.current); + timeout.current = setTimeout(() => isMounted() && setHasCopied(false), 500); + } + + return ( +
+
+

+ {t("auth.generate.passphraseFrameLabel")} +

+ +
+
+ {individualWords.map((word, i) => ( +
+ {word} +
+ ))} +
+
+ ); +} diff --git a/src/components/form/SearchBar.tsx b/src/components/form/SearchBar.tsx new file mode 100644 index 00000000..6ac6128f --- /dev/null +++ b/src/components/form/SearchBar.tsx @@ -0,0 +1,78 @@ +import c from "classnames"; +import { forwardRef, useState } from "react"; + +import { Flare } from "@/components/utils/Flare"; + +import { Icon, Icons } from "../Icon"; +import { TextInputControl } from "../text-inputs/TextInputControl"; + +export interface SearchBarProps { + placeholder?: string; + onChange: (value: string, force: boolean) => void; + onUnFocus: (newSearch?: string) => void; + value: string; +} + +export const SearchBarInput = forwardRef( + (props, ref) => { + const [focused, setFocused] = useState(false); + + function setSearch(value: string) { + props.onChange(value, false); + } + + return ( + + + +
+ +
+ + { + setFocused(false); + props.onUnFocus(); + }} + onFocus={() => setFocused(true)} + onChange={(val) => setSearch(val)} + value={props.value} + className="w-full flex-1 bg-transparent px-4 py-4 pl-12 text-search-text placeholder-search-placeholder focus:outline-none sm:py-4 sm:pr-2" + placeholder={props.placeholder} + /> + + {props.value.length > 0 && ( +
{ + props.onUnFocus(""); + if (ref && typeof ref !== "function") { + ref.current?.focus(); + } + }} + className="cursor-pointer hover:text-white absolute bottom-0 right-2 top-0 flex justify-center my-auto h-10 w-10 items-center hover:bg-search-hoverBackground active:scale-110 text-search-icon rounded-full transition-[transform,background-color] duration-200" + > + +
+ )} +
+
+ ); + }, +); diff --git a/src/components/layout/Box.tsx b/src/components/layout/Box.tsx new file mode 100644 index 00000000..d1e30026 --- /dev/null +++ b/src/components/layout/Box.tsx @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +export function Box(props: { children?: ReactNode }) { + return ( +
+ {props.children} +
+ ); +} diff --git a/src/components/layout/BrandPill.tsx b/src/components/layout/BrandPill.tsx new file mode 100644 index 00000000..109dbd2c --- /dev/null +++ b/src/components/layout/BrandPill.tsx @@ -0,0 +1,34 @@ +import classNames from "classnames"; +import { useTranslation } from "react-i18next"; + +import { Icon, Icons } from "@/components/Icon"; + +export function BrandPill(props: { + clickable?: boolean; + hideTextOnMobile?: boolean; + backgroundClass?: string; +}) { + const { t } = useTranslation(); + + return ( +
+ + + {t("global.name")} + +
+ ); +} diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx new file mode 100644 index 00000000..44c61058 --- /dev/null +++ b/src/components/layout/Footer.tsx @@ -0,0 +1,109 @@ +import { useCallback } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import type { RequireExactlyOne } from "type-fest"; + +import { Icon, Icons } from "@/components/Icon"; +import { BrandPill } from "@/components/layout/BrandPill"; +import { WideContainer } from "@/components/layout/WideContainer"; +import { shouldHaveDmcaPage } from "@/pages/Dmca"; +import { conf } from "@/setup/config"; + +// to and href are mutually exclusive +type FooterLinkProps = RequireExactlyOne< + { + children: React.ReactNode; + icon: Icons; + to: string; + href: string; + }, + "to" | "href" +>; + +function FooterLink(props: FooterLinkProps) { + const navigate = useNavigate(); + + const navigateTo = useCallback(() => { + if (!props.to) return; + + navigate(props.to); + }, [navigate, props.to]); + + return ( + + + {props.children} + + ); +} + +function Dmca() { + const { t } = useTranslation(); + + if (!shouldHaveDmcaPage()) return null; + + return ( + + {t("footer.links.dmca")} + + ); +} + +export function Footer() { + const { t } = useTranslation(); + + return ( +
+ +
+
+ +
+

{t("footer.tagline")}

+
+
+

+ {t("footer.legal.disclaimer")} +

+

{t("footer.legal.disclaimerText")}

+
+
+ + {t("footer.links.github")} + + + {t("footer.links.discord")} + +
+ +
+
+
+ +
+
+
+ ); +} + +export function FooterView(props: { + children: React.ReactNode; + className?: string; +}) { + return ( +
+
{props.children}
+
+
+ ); +} diff --git a/src/components/layout/IconPill.tsx b/src/components/layout/IconPill.tsx new file mode 100644 index 00000000..6530e773 --- /dev/null +++ b/src/components/layout/IconPill.tsx @@ -0,0 +1,13 @@ +import { Icon, Icons } from "@/components/Icon"; + +export function IconPill(props: { icon: Icons; children?: React.ReactNode }) { + return ( +
+ + {props.children} +
+ ); +} diff --git a/src/components/layout/LargeCard.tsx b/src/components/layout/LargeCard.tsx new file mode 100644 index 00000000..dbc010af --- /dev/null +++ b/src/components/layout/LargeCard.tsx @@ -0,0 +1,58 @@ +import classNames from "classnames"; + +export function LargeCard(props: { + children: React.ReactNode; + top?: React.ReactNode; +}) { + return ( +
+ {props.top ? ( +
+ {props.top} +
+ ) : null} +
+ {props.children} +
+
+ ); +} + +export function LargeCardText(props: { + title: string; + children?: React.ReactNode; + icon?: React.ReactNode; +}) { + return ( +
+
+ {props.icon ? ( +
{props.icon}
+ ) : null} +

{props.title}

+ {props.children ? ( +
{props.children}
+ ) : null} +
+
+ ); +} + +export function LargeCardButtons(props: { + children: React.ReactNode; + splitAlign?: boolean; +}) { + return ( +
+
+ {props.children} +
+
+ ); +} diff --git a/src/components/layout/Loading.tsx b/src/components/layout/Loading.tsx new file mode 100644 index 00000000..4c4c62bd --- /dev/null +++ b/src/components/layout/Loading.tsx @@ -0,0 +1,22 @@ +export interface LoadingProps { + text?: string; + className?: string; +} + +export function Loading(props: LoadingProps) { + return ( +
+
+
+
+
+
+
+
+ {props.text && props.text.length ? ( +

{props.text}

+ ) : null} +
+
+ ); +} diff --git a/src/components/layout/Navigation.tsx b/src/components/layout/Navigation.tsx new file mode 100644 index 00000000..d9398aba --- /dev/null +++ b/src/components/layout/Navigation.tsx @@ -0,0 +1,116 @@ +import classNames from "classnames"; +import { Link } from "react-router-dom"; + +import { NoUserAvatar, UserAvatar } from "@/components/Avatar"; +import { IconPatch } from "@/components/buttons/IconPatch"; +import { Icons } from "@/components/Icon"; +import { LinksDropdown } from "@/components/LinksDropdown"; +import { Lightbar } from "@/components/utils/Lightbar"; +import { useAuth } from "@/hooks/auth/useAuth"; +import { BlurEllipsis } from "@/pages/layouts/SubPageLayout"; +import { conf } from "@/setup/config"; +import { useBannerSize } from "@/stores/banner"; + +import { BrandPill } from "./BrandPill"; + +export interface NavigationProps { + bg?: boolean; + noLightbar?: boolean; + doBackground?: boolean; +} + +export function Navigation(props: NavigationProps) { + const bannerHeight = useBannerSize(); + const { loggedIn } = useAuth(); + + return ( + <> + {/* lightbar */} + {!props.noLightbar ? ( +
+
+ +
+
+ ) : null} + + {/* backgrounds - these are seperate because of z-index issues */} +
+
+ {props.doBackground ? ( +
+ +
+ ) : null} +
+
+
+
+
+
+ + {/* content */} +
+
+
+
+ + + + + + + + + +
+
+ + {loggedIn ? : } + +
+
+
+
+ + ); +} diff --git a/src/components/layout/ProgressRing.tsx b/src/components/layout/ProgressRing.tsx new file mode 100644 index 00000000..d0e680a9 --- /dev/null +++ b/src/components/layout/ProgressRing.tsx @@ -0,0 +1,39 @@ +interface Props { + className?: string; + radius?: number; + percentage: number; + backingRingClassname?: string; +} + +export function ProgressRing(props: Props) { + const radius = props.radius ?? 40; + + return ( + + + + + ); +} diff --git a/src/components/layout/SectionHeading.tsx b/src/components/layout/SectionHeading.tsx new file mode 100644 index 00000000..ef994557 --- /dev/null +++ b/src/components/layout/SectionHeading.tsx @@ -0,0 +1,28 @@ +import { ReactNode } from "react"; + +import { Icon, Icons } from "@/components/Icon"; + +interface SectionHeadingProps { + icon?: Icons; + title: string; + children?: ReactNode; + className?: string; +} + +export function SectionHeading(props: SectionHeadingProps) { + return ( +
+
+

+ {props.icon ? ( + + + + ) : null} + {props.title} +

+ {props.children} +
+
+ ); +} diff --git a/src/components/layout/SettingsCard.tsx b/src/components/layout/SettingsCard.tsx new file mode 100644 index 00000000..4be0ce9e --- /dev/null +++ b/src/components/layout/SettingsCard.tsx @@ -0,0 +1,37 @@ +import classNames from "classnames"; + +export function SettingsCard(props: { + children: React.ReactNode; + className?: string; + paddingClass?: string; +}) { + return ( +
+ {props.children} +
+ ); +} + +export function SolidSettingsCard(props: { + children: React.ReactNode; + className?: string; + paddingClass?: string; +}) { + return ( +
+ {props.children} +
+ ); +} diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx new file mode 100644 index 00000000..f5c2a176 --- /dev/null +++ b/src/components/layout/Sidebar.tsx @@ -0,0 +1,47 @@ +import classNames from "classnames"; + +import { Icon, Icons } from "@/components/Icon"; + +export function SidebarSection(props: { + title: string; + children: React.ReactNode; + className?: string; +}) { + return ( +
+

+ {props.title} +

+ {props.children} +
+ ); +} + +export function SidebarLink(props: { + children: React.ReactNode; + icon: Icons; + active?: boolean; + onClick?: () => void; +}) { + return ( + + ); +} diff --git a/src/components/layout/Spinner.css b/src/components/layout/Spinner.css new file mode 100644 index 00000000..aa7cc1bc --- /dev/null +++ b/src/components/layout/Spinner.css @@ -0,0 +1,19 @@ +.spinner { + width: 1em; + height: 1em; + border: 0.12em solid var(--color,white); + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + box-sizing: border-box; + animation: spinner-rotation 800ms linear infinite; +} + +@keyframes spinner-rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/layout/Spinner.tsx b/src/components/layout/Spinner.tsx new file mode 100644 index 00000000..77e06cfd --- /dev/null +++ b/src/components/layout/Spinner.tsx @@ -0,0 +1,9 @@ +import "./Spinner.css"; + +interface SpinnerProps { + className?: string; +} + +export function Spinner(props: SpinnerProps) { + return
; +} diff --git a/src/components/layout/Stepper.tsx b/src/components/layout/Stepper.tsx new file mode 100644 index 00000000..75dc150b --- /dev/null +++ b/src/components/layout/Stepper.tsx @@ -0,0 +1,25 @@ +export interface StepperProps { + current: number; + steps: number; + className?: string; +} + +export function Stepper(props: StepperProps) { + const percentage = (props.current / props.steps) * 100; + + return ( +
+

+ {props.current}/{props.steps} +

+
+
+
+
+ ); +} diff --git a/src/components/layout/ThinContainer.tsx b/src/components/layout/ThinContainer.tsx new file mode 100644 index 00000000..9f06f579 --- /dev/null +++ b/src/components/layout/ThinContainer.tsx @@ -0,0 +1,32 @@ +import classNames from "classnames"; +import { ReactNode } from "react"; + +interface ThinContainerProps { + classNames?: string; + children?: ReactNode; +} + +export function ThinContainer(props: ThinContainerProps) { + return ( +
+ {props.children} +
+ ); +} + +export function CenterContainer(props: ThinContainerProps) { + return ( +
+
{props.children}
+
+ ); +} diff --git a/src/components/layout/WideContainer.tsx b/src/components/layout/WideContainer.tsx new file mode 100644 index 00000000..bcccd5e5 --- /dev/null +++ b/src/components/layout/WideContainer.tsx @@ -0,0 +1,19 @@ +import { ReactNode } from "react"; + +interface WideContainerProps { + classNames?: string; + children?: ReactNode; + ultraWide?: boolean; +} + +export function WideContainer(props: WideContainerProps) { + return ( +
+ {props.children} +
+ ); +} diff --git a/src/components/media/MediaCard.tsx b/src/components/media/MediaCard.tsx new file mode 100644 index 00000000..cad3ae6a --- /dev/null +++ b/src/components/media/MediaCard.tsx @@ -0,0 +1,173 @@ +import classNames from "classnames"; +import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; + +import { mediaItemToId } from "@/backend/metadata/tmdb"; +import { DotList } from "@/components/text/DotList"; +import { Flare } from "@/components/utils/Flare"; +import { MediaItem } from "@/utils/mediaTypes"; + +import { IconPatch } from "../buttons/IconPatch"; +import { Icons } from "../Icon"; + +export interface MediaCardProps { + media: MediaItem; + linkable?: boolean; + series?: { + episode: number; + season?: number; + episodeId: string; + seasonId: string; + }; + percentage?: number; + closable?: boolean; + onClose?: () => void; +} + +function MediaCardContent({ + media, + linkable, + series, + percentage, + closable, + onClose, +}: MediaCardProps) { + const { t } = useTranslation(); + const percentageString = `${Math.round(percentage ?? 0).toFixed(0)}%`; + + const canLink = linkable && !closable; + + const dotListContent = [t(`media.types.${media.type}`)]; + if (media.year) dotListContent.push(media.year.toFixed()); + + return ( + e.key === "Enter" && e.currentTarget.click()} + > + + +
+ {series ? ( +
+

+ {t("media.episodeDisplay", { + season: series.season || 1, + episode: series.episode, + })} +

+
+ ) : null} + + {percentage !== undefined ? ( + <> +
+
+
+
+
+
+
+ + ) : null} + +
+ closable && onClose?.()} + icon={Icons.X} + /> +
+
+

+ {media.title} +

+ + + + ); +} + +export function MediaCard(props: MediaCardProps) { + const content = ; + + const canLink = props.linkable && !props.closable; + + let link = canLink + ? `/media/${encodeURIComponent(mediaItemToId(props.media))}` + : "#"; + if (canLink && props.series) { + if (props.series.season === 0 && !props.series.episodeId) { + link += `/${encodeURIComponent(props.series.seasonId)}`; + } else { + link += `/${encodeURIComponent( + props.series.seasonId, + )}/${encodeURIComponent(props.series.episodeId)}`; + } + } + + if (!props.linkable) return {content}; + return ( + + {content} + + ); +} diff --git a/src/components/media/MediaGrid.tsx b/src/components/media/MediaGrid.tsx new file mode 100644 index 00000000..adb9b932 --- /dev/null +++ b/src/components/media/MediaGrid.tsx @@ -0,0 +1,18 @@ +import { forwardRef } from "react"; + +interface MediaGridProps { + children?: React.ReactNode; +} + +export const MediaGrid = forwardRef( + (props, ref) => { + return ( +
+ {props.children} +
+ ); + }, +); diff --git a/src/components/media/WatchedMediaCard.tsx b/src/components/media/WatchedMediaCard.tsx new file mode 100644 index 00000000..b7640efd --- /dev/null +++ b/src/components/media/WatchedMediaCard.tsx @@ -0,0 +1,51 @@ +import { useMemo } from "react"; + +import { useProgressStore } from "@/stores/progress"; +import { + ShowProgressResult, + shouldShowProgress, +} from "@/stores/progress/utils"; +import { MediaItem } from "@/utils/mediaTypes"; + +import { MediaCard } from "./MediaCard"; + +function formatSeries(series?: ShowProgressResult | null) { + if (!series || !series.episode || !series.season) return undefined; + return { + episode: series.episode?.number, + season: series.season?.number, + episodeId: series.episode?.id, + seasonId: series.season?.id, + }; +} + +export interface WatchedMediaCardProps { + media: MediaItem; + closable?: boolean; + onClose?: () => void; +} + +export function WatchedMediaCard(props: WatchedMediaCardProps) { + const progressItems = useProgressStore((s) => s.items); + const item = useMemo(() => { + return progressItems[props.media.id]; + }, [progressItems, props.media]); + const itemToDisplay = useMemo( + () => (item ? shouldShowProgress(item) : null), + [item], + ); + const percentage = itemToDisplay?.show + ? (itemToDisplay.progress.watched / itemToDisplay.progress.duration) * 100 + : undefined; + + return ( + + ); +} diff --git a/src/components/overlays/Modal.tsx b/src/components/overlays/Modal.tsx new file mode 100644 index 00000000..08abdbba --- /dev/null +++ b/src/components/overlays/Modal.tsx @@ -0,0 +1,42 @@ +import { ReactNode, useCallback } from "react"; +import { Helmet } from "react-helmet-async"; + +import { OverlayPortal } from "@/components/overlays/OverlayDisplay"; +import { useQueryParam } from "@/hooks/useQueryParams"; + +export function useModal(id: string) { + const [currentModal, setCurrentModal] = useQueryParam("m"); + const show = useCallback(() => setCurrentModal(id), [id, setCurrentModal]); + const hide = useCallback(() => setCurrentModal(null), [setCurrentModal]); + return { + id, + isShown: currentModal === id, + show, + hide, + }; +} + +export function ModalCard(props: { children?: ReactNode }) { + return ( +
+
+ {props.children} +
+
+ ); +} + +export function Modal(props: { id: string; children?: ReactNode }) { + const modal = useModal(props.id); + + return ( + + + + +
+ {props.children} +
+
+ ); +} diff --git a/src/components/overlays/OverlayAnchor.tsx b/src/components/overlays/OverlayAnchor.tsx new file mode 100644 index 00000000..4939ab1d --- /dev/null +++ b/src/components/overlays/OverlayAnchor.tsx @@ -0,0 +1,20 @@ +import classNames from "classnames"; +import { ReactNode } from "react"; + +interface Props { + id: string; + children?: ReactNode; + className?: string; +} + +export function OverlayAnchor(props: Props) { + return ( +
+
+ {props.children} +
+ ); +} diff --git a/src/components/overlays/OverlayDisplay.tsx b/src/components/overlays/OverlayDisplay.tsx new file mode 100644 index 00000000..048ec0e7 --- /dev/null +++ b/src/components/overlays/OverlayDisplay.tsx @@ -0,0 +1,141 @@ +import classNames from "classnames"; +import FocusTrap from "focus-trap-react"; +import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; +import { createPortal } from "react-dom"; +import { useTranslation } from "react-i18next"; + +import { Transition } from "@/components/utils/Transition"; +import { + useInternalOverlayRouter, + useRouterAnchorUpdate, +} from "@/hooks/useOverlayRouter"; +import { TurnstileProvider } from "@/stores/turnstile"; + +export interface OverlayProps { + id: string; + children?: ReactNode; + darken?: boolean; +} + +function TurnstileInteractive() { + const { t } = useTranslation(); + const [show, setShow] = useState(false); + + // this may not rerender with different dom structure, must be exactly the same always + return ( +
+
+
+

+ {t("player.turnstile.title")} +

+

{t("player.turnstile.description")}

+
+ setShow(shouldShow)} + /> +
+
+ ); +} + +export function OverlayDisplay(props: { children: ReactNode }) { + const router = useInternalOverlayRouter("hello world :)"); + const refRouter = useRef(router); + + // close router on first mount, we dont want persist routes for overlays + useEffect(() => { + const r = refRouter.current; + r.close(); + return () => { + r.close(); + }; + }, []); + return ( +
+ + {props.children} +
+ ); +} + +export function OverlayPortal(props: { + children?: ReactNode; + darken?: boolean; + show?: boolean; + close?: () => void; +}) { + const [portalElement, setPortalElement] = useState(null); + const ref = useRef(null); + const close = props.close; + + useEffect(() => { + const element = ref.current?.closest(".popout-location"); + setPortalElement(element ?? document.body); + }, []); + + return ( +
+ {portalElement + ? createPortal( + + +
+ +
+ + + {/* a tabable index that does nothing - used so focus trap doesn't error when nothing is rendered yet */} +
+ {props.children} + +
+ +
, + portalElement, + ) + : null} +
+ ); +} + +export function Overlay(props: OverlayProps) { + const router = useInternalOverlayRouter(props.id); + const realClose = router.close; + + // listen for anchor updates + useRouterAnchorUpdate(props.id); + + const close = useCallback(() => { + realClose(); + }, [realClose]); + + return ( + + {props.children} + + ); +} diff --git a/src/components/overlays/OverlayPage.tsx b/src/components/overlays/OverlayPage.tsx new file mode 100644 index 00000000..f15ebaa1 --- /dev/null +++ b/src/components/overlays/OverlayPage.tsx @@ -0,0 +1,60 @@ +import classNames from "classnames"; +import { ReactNode, useEffect, useMemo } from "react"; + +import { + Transition, + TransitionAnimations, +} from "@/components/utils/Transition"; +import { useIsMobile } from "@/hooks/useIsMobile"; +import { useInternalOverlayRouter } from "@/hooks/useOverlayRouter"; +import { useOverlayStore } from "@/stores/overlay/store"; + +interface Props { + id: string; + path: string; + children?: ReactNode; + className?: string; + height: number; + width: number; +} + +export function OverlayPage(props: Props) { + const router = useInternalOverlayRouter(props.id); + const backwards = router.showBackwardsTransition(props.path); + const show = router.isCurrentPage(props.path); + const registerRoute = useOverlayStore((s) => s.registerRoute); + const path = useMemo(() => router.makePath(props.path), [props.path, router]); + const { isMobile } = useIsMobile(); + + useEffect(() => { + registerRoute({ + id: path, + width: props.width, + height: props.height, + }); + }, [props.height, props.width, path, registerRoute]); + + const width = !isMobile ? `${props.width}px` : "100%"; + let animation: TransitionAnimations = "none"; + if (backwards === "yes" || backwards === "no") + animation = backwards === "yes" ? "slide-full-left" : "slide-full-right"; + + return ( + +
+ {props.children} +
+
+ ); +} diff --git a/src/components/overlays/OverlayRouter.tsx b/src/components/overlays/OverlayRouter.tsx new file mode 100644 index 00000000..dc36926d --- /dev/null +++ b/src/components/overlays/OverlayRouter.tsx @@ -0,0 +1,101 @@ +import { a, easings, useSpring } from "@react-spring/web"; +import { ReactNode, useEffect, useMemo, useRef } from "react"; + +import { OverlayAnchorPosition } from "@/components/overlays/positions/OverlayAnchorPosition"; +import { OverlayMobilePosition } from "@/components/overlays/positions/OverlayMobilePosition"; +import { Flare } from "@/components/utils/Flare"; +import { useIsMobile } from "@/hooks/useIsMobile"; +import { useInternalOverlayRouter } from "@/hooks/useOverlayRouter"; +import { useOverlayStore } from "@/stores/overlay/store"; + +interface OverlayRouterProps { + children?: ReactNode; + id: string; +} + +function RouterBase(props: { id: string; children: ReactNode }) { + const ref = useRef(null); + const { isMobile } = useIsMobile(); + + const routes = useOverlayStore((s) => s.routes); + const router = useInternalOverlayRouter(props.id); + const routeMeta = useMemo( + () => routes[router.currentRoute ?? ""], + [routes, router], + ); + + const [dimensions, api] = useSpring( + () => ({ + from: { + height: `${routeMeta?.height ?? 0}px`, + width: isMobile ? "100%" : `${routeMeta?.width ?? 0}px`, + }, + config: { + easing: easings.linear, + }, + }), + [], + ); + + const currentState = useRef(null); + useEffect(() => { + const data = { + height: routeMeta?.height, + width: routeMeta?.width, + isMobile, + }; + const dataStr = JSON.stringify(data); + if (dataStr !== currentState.current) { + const oldData = currentState.current + ? JSON.parse(currentState.current) + : null; + currentState.current = dataStr; + if (data.isMobile) { + api.set({ + width: "100%", + }); + api.start({ + height: `${routeMeta?.height ?? 0}px`, + }); + } else if (oldData?.height === undefined && data.height !== undefined) { + api.set({ + height: `${routeMeta?.height ?? 0}px`, + width: `${routeMeta?.width ?? 0}px`, + }); + } else { + api.start({ + height: `${routeMeta?.height ?? 0}px`, + width: `${routeMeta?.width ?? 0}px`, + }); + } + } + }, [routeMeta?.height, routeMeta?.width, isMobile, api]); + + return ( + + + + + {props.children} + + + + ); +} + +export function OverlayRouter(props: OverlayRouterProps) { + const { isMobile } = useIsMobile(); + const content = {props.children}; + + if (isMobile) return {content}; + return {content}; +} diff --git a/src/components/overlays/positions/OverlayAnchorPosition.tsx b/src/components/overlays/positions/OverlayAnchorPosition.tsx new file mode 100644 index 00000000..f4120d94 --- /dev/null +++ b/src/components/overlays/positions/OverlayAnchorPosition.tsx @@ -0,0 +1,74 @@ +import classNames from "classnames"; +import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; + +import { useOverlayStore } from "@/stores/overlay/store"; + +interface AnchorPositionProps { + children?: ReactNode; + className?: string; +} + +function useCalculatePositions() { + const anchorPoint = useOverlayStore((s) => s.anchorPoint); + const ref = useRef(null); + const [left, setLeft] = useState(0); + const [top, setTop] = useState(0); + const [cardRect, setCardRect] = useState(null); + + const calculateAndSetCoords = useCallback( + (anchor: typeof anchorPoint, card: DOMRect) => { + if (!anchor) return; + const buttonCenter = anchor.x + anchor.w / 2; + const bottomReal = window.innerHeight - (anchor.y + anchor.h); + + setTop(window.innerHeight - bottomReal - anchor.h - card.height - 30); + setLeft( + Math.min( + buttonCenter - card.width / 2, + window.innerWidth - card.width - 30, + ), + ); + }, + [], + ); + + useEffect(() => { + if (!anchorPoint || !cardRect) return; + calculateAndSetCoords(anchorPoint, cardRect); + }, [anchorPoint, calculateAndSetCoords, cardRect]); + + useEffect(() => { + if (!ref.current) return; + function checkBox() { + const divRect = ref.current?.getBoundingClientRect(); + setCardRect(divRect ?? null); + } + checkBox(); + const observer = new ResizeObserver(checkBox); + observer.observe(ref.current); + return () => { + observer.disconnect(); + }; + }, []); + + return [ref, left, top] as const; +} + +export function OverlayAnchorPosition(props: AnchorPositionProps) { + const [ref, left, top] = useCalculatePositions(); + + return ( +
*]: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, + ])} + > + {props.children} +
+ ); +} diff --git a/src/components/overlays/positions/OverlayMobilePosition.tsx b/src/components/overlays/positions/OverlayMobilePosition.tsx new file mode 100644 index 00000000..53b21000 --- /dev/null +++ b/src/components/overlays/positions/OverlayMobilePosition.tsx @@ -0,0 +1,37 @@ +import classNames from "classnames"; +import { ReactNode } from "react"; +import { useTranslation } from "react-i18next"; + +import { useInternalOverlayRouter } from "@/hooks/useOverlayRouter"; + +interface MobilePositionProps { + children?: ReactNode; + className?: string; +} + +export function OverlayMobilePosition(props: MobilePositionProps) { + const router = useInternalOverlayRouter("hello world :)"); + const { t } = useTranslation(); + + return ( +
+ {props.children} + + {/* Close button */} + + {/* Gradient to hide the progress */} +
+
+ ); +} diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx new file mode 100644 index 00000000..df8e807a --- /dev/null +++ b/src/components/player/Player.tsx @@ -0,0 +1,11 @@ +export * from "./atoms"; +export * from "./base/Container"; +export * from "./base/TopControls"; +export * from "./base/CenterControls"; +export * from "./base/BottomControls"; +export * from "./base/BlackOverlay"; +export * from "./base/BackLink"; +export * from "./base/LeftSideControls"; +export * from "./base/CenterMobileControls"; +export * from "./base/SubtitleView"; +export * from "./internals/BookmarkButton"; diff --git a/src/components/player/README.md b/src/components/player/README.md new file mode 100644 index 00000000..3e3bc0c6 --- /dev/null +++ b/src/components/player/README.md @@ -0,0 +1,31 @@ +# Video player component + +Video player is quite a complex component, so here is a rundown of all the parts + +# Composable parts +These parts can be used to build any shape of a video player. + - `/atoms`- any ui element that controls the player. (Seekbar, Pause button, quality selection, etc) + - `/base` - base components that are used to build a player. Like the main container + +# internal parts +These parts are internally used, they aren't exported. Do not use them outside of player internals. + +### `/display` +The display interface, abstraction on how to actually play the content (e.g Video element, chrome casting, etc) + - It must be completely separate from any react code + - It must not interact with state, pass async data back with events + +### `/internals` +Internal components that are always rendered on every player. + - Only components that are always present on the player instance, they must never unmount + +### `/utils` +miscellaneous logic, put anything that is unique to the video player internals. + +### `/hooks` +Hooks only used for video player. + - only exception is usePlayer, as its used outside of the player to control the player + +### `~/src/stores/player` +State for the video player. + - Only parts related to the video player may utilize the state diff --git a/src/components/player/atoms/Airplay.tsx b/src/components/player/atoms/Airplay.tsx new file mode 100644 index 00000000..0d517fc8 --- /dev/null +++ b/src/components/player/atoms/Airplay.tsx @@ -0,0 +1,17 @@ +import { Icons } from "@/components/Icon"; +import { VideoPlayerButton } from "@/components/player/internals/Button"; +import { usePlayerStore } from "@/stores/player/store"; + +export function Airplay() { + const canAirplay = usePlayerStore((s) => s.interface.canAirplay); + const display = usePlayerStore((s) => s.display); + + if (!canAirplay) return null; + + return ( + display?.startAirplay()} + icon={Icons.AIRPLAY} + /> + ); +} diff --git a/src/components/player/atoms/AutoPlayStart.tsx b/src/components/player/atoms/AutoPlayStart.tsx new file mode 100644 index 00000000..7ae99e88 --- /dev/null +++ b/src/components/player/atoms/AutoPlayStart.tsx @@ -0,0 +1,34 @@ +import { useCallback } from "react"; + +import { Icon, Icons } from "@/components/Icon"; +import { playerStatus } from "@/stores/player/slices/source"; +import { usePlayerStore } from "@/stores/player/store"; + +export function AutoPlayStart() { + const display = usePlayerStore((s) => s.display); + const isPlaying = usePlayerStore((s) => s.mediaPlaying.isPlaying); + const isLoading = usePlayerStore((s) => s.mediaPlaying.isLoading); + const hasPlayedOnce = usePlayerStore((s) => s.mediaPlaying.hasPlayedOnce); + const status = usePlayerStore((s) => s.status); + + const handleClick = useCallback(() => { + display?.play(); + }, [display]); + + if (hasPlayedOnce) return null; + if (isPlaying) return null; + if (isLoading) return null; + if (status !== playerStatus.PLAYING) return null; + + return ( +
+ +
+ ); +} diff --git a/src/components/player/atoms/CastingNotification.tsx b/src/components/player/atoms/CastingNotification.tsx new file mode 100644 index 00000000..9c4e423b --- /dev/null +++ b/src/components/player/atoms/CastingNotification.tsx @@ -0,0 +1,22 @@ +import { useTranslation } from "react-i18next"; + +import { Icon, Icons } from "@/components/Icon"; +import { usePlayerStore } from "@/stores/player/store"; + +export function CastingNotification() { + const { t } = useTranslation(); + const isLoading = usePlayerStore((s) => s.mediaPlaying.isLoading); + const display = usePlayerStore((s) => s.display); + const isCasting = display?.getType() === "casting"; + + if (isLoading || !isCasting) return null; + + return ( +
+
+ +
+

{t("player.casting.enabled")}

+
+ ); +} diff --git a/src/components/player/atoms/Chromecast.tsx b/src/components/player/atoms/Chromecast.tsx new file mode 100644 index 00000000..a3d278e8 --- /dev/null +++ b/src/components/player/atoms/Chromecast.tsx @@ -0,0 +1,56 @@ +import { useCallback, useEffect, useRef, useState } from "react"; + +import { Icons } from "@/components/Icon"; +import { VideoPlayerButton } from "@/components/player/internals/Button"; +import { usePlayerStore } from "@/stores/player/store"; + +export interface ChromecastProps { + className?: string; +} + +export function Chromecast(props: ChromecastProps) { + const [hidden, setHidden] = useState(false); + const isCasting = usePlayerStore((s) => s.interface.isCasting); + const ref = useRef(null); + + const setButtonVisibility = useCallback( + (tag: HTMLElement) => { + const isVisible = (tag.getAttribute("style") ?? "").includes("inline"); + setHidden(!isVisible); + }, + [setHidden], + ); + + useEffect(() => { + const tag = ref.current?.querySelector("google-cast-launcher"); + if (!tag) return; + + const observer = new MutationObserver(() => { + setButtonVisibility(tag); + }); + + observer.observe(tag, { attributes: true, attributeFilter: ["style"] }); + setButtonVisibility(tag); + + return () => { + observer.disconnect(); + }; + }, [setButtonVisibility]); + + return ( +