├── .actrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── build_failures.md │ ├── config.yml │ └── feature_request.yml └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── njsscan.yml │ ├── potential-duplicates.yml │ ├── prerelease.yml │ └── release.yml ├── .gitignore ├── .husky ├── core.sh └── pre-commit ├── .npmrc ├── LICENSE ├── docs ├── Build.md ├── Contributing.md ├── FAQ.md ├── Features.md ├── Files.md ├── Flags.md ├── Privacy.md ├── Readme.md ├── Repos.md ├── Support.md └── pl │ └── Readme.md ├── package-lock.json ├── package.json ├── schemas ├── localization │ └── settings.json └── schemastore │ ├── LICENSE │ ├── NOTICE │ └── package.json ├── sources ├── assets │ ├── icons │ │ ├── app.icns │ │ ├── app.ico │ │ ├── app.png │ │ ├── symbols │ │ │ ├── checkmark.svg │ │ │ ├── close.svg │ │ │ ├── menu.svg │ │ │ └── speaker.svg │ │ ├── tray-ping.png │ │ ├── tray-unread.png │ │ └── tray.png │ └── web │ │ ├── css │ │ ├── about.css │ │ ├── capturer.css │ │ ├── discord.css │ │ ├── fonts.css │ │ ├── global.css │ │ ├── load.css │ │ ├── markdown.css │ │ └── settings.css │ │ ├── html │ │ ├── 404.html │ │ ├── about.html │ │ ├── capturer.html │ │ ├── docs.html │ │ ├── load.html │ │ └── settings.html │ │ └── json │ │ └── motd.json ├── code │ ├── build │ │ └── forge.mts │ ├── common │ │ ├── global.ts │ │ ├── main.ts │ │ └── modules │ │ │ ├── agent.ts │ │ │ ├── client.ts │ │ │ ├── electron.ts │ │ │ ├── l10n.ts │ │ │ └── package.ts │ ├── main │ │ ├── modules │ │ │ ├── bug.ts │ │ │ ├── config.ts │ │ │ ├── csp.ts │ │ │ ├── error.ts │ │ │ ├── extensions.ts │ │ │ ├── menu.ts │ │ │ ├── optimize.ts │ │ │ ├── parent.ts │ │ │ ├── socket.mts │ │ │ └── update.ts │ │ └── windows │ │ │ ├── about.ts │ │ │ ├── docs.ts │ │ │ ├── main.ts │ │ │ └── settings.ts │ ├── renderer │ │ ├── modules │ │ │ └── api.ts │ │ └── preload │ │ │ ├── about.ts │ │ │ ├── capturer.ts │ │ │ ├── docs.ts │ │ │ ├── main.ts │ │ │ └── settings.ts │ └── utility │ │ └── ws.ts └── translations │ ├── ar │ ├── client.json │ ├── settings.json │ └── web.json │ ├── ca │ ├── client.json │ ├── settings.json │ └── web.json │ ├── de │ ├── client.json │ ├── settings.json │ └── web.json │ ├── el │ ├── client.json │ ├── settings.json │ └── web.json │ ├── en │ ├── client.json │ ├── settings.json │ └── web.json │ ├── es-MX │ ├── client.json │ ├── settings.json │ └── web.json │ ├── es │ ├── client.json │ ├── settings.json │ └── web.json │ ├── fr │ ├── client.json │ ├── settings.json │ └── web.json │ ├── it │ ├── client.json │ ├── settings.json │ └── web.json │ ├── ja │ ├── client.json │ ├── settings.json │ └── web.json │ ├── nb │ ├── client.json │ ├── settings.json │ └── web.json │ ├── nl │ ├── client.json │ ├── settings.json │ └── web.json │ ├── pl │ ├── client.json │ ├── settings.json │ └── web.json │ ├── pt │ ├── client.json │ ├── settings.json │ └── web.json │ ├── ru │ ├── client.json │ ├── settings.json │ └── web.json │ ├── ta │ ├── client.json │ ├── settings.json │ └── web.json │ ├── tr │ ├── client.json │ ├── settings.json │ └── web.json │ └── uk │ ├── client.json │ ├── settings.json │ └── web.json └── tsconfig.json /.actrc: -------------------------------------------------------------------------------- 1 | # Workaround for an issue with act's invalid `.gitignore` parsing. 2 | --use-gitignore=false -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{ts,json}] 4 | charset = utf-8 5 | trim_trailing_whitespace = true 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = false 10 | 11 | [package{,-lock}.json] 12 | insert_final_newline = true 13 | 14 | [tsconfig.json] 15 | indent_size = 4 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | app/ 2 | node_modules/ 3 | cache/ 4 | .* -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.png binary 3 | *.ico binary 4 | *.icns binary -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/build_failures.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build failure report. 3 | about: Report issues when building / packaging the application. 4 | title: '' 5 | labels: 'type:build' 6 | assignees: SpacingBat3 7 | 8 | --- 9 | 10 | 19 | 20 | ### Acknowledgements ### 21 | 22 | - [ ] There's no other issue describing the same problem, regardless of its current state (i.e. including both closed and open issues).\* 23 | - [ ] There's no fix for my issue released to `master` branch.\* 24 | - [ ] This issue has known workaround (write it below). 25 | 26 | * Required 27 | 28 | ### Environment ### 29 | 33 | - Platform: [e.g. `🐧️ linux`] 34 | - Architecture: [e.g `arm64`/`aarch64`] 35 | - Electron version: [e.g. `v12.0.7`] 36 | - Electron Forge version: [e.g. `v6.3.0`] 37 | - Application version: [e.g. `v4.4.0`] 38 | - TypeScript compiler version: [e.g. `5.0.0`] 39 | - Node.js package manager: [e.g. `npm`] 40 | - Node.js package manger version: [e.g. `9.8.1`] 41 | 42 | ### Describe the problem ### 43 | A clear and concise description of where the build and package process fails. 44 | 45 | ### To Reproduce ### 46 | Steps to reproduce the behaviour: 47 | 1. Go to '...' 48 | 2. Click on '....' 49 | 3. Scroll down to '....' 50 | 4. See error. 51 | 52 | ### Expected behaviour ### 53 | A clear and concise description of what you expected to happen. 54 | 55 | ### Screenshots ### 56 | If applicable, add screenshots to help explain your problem. 57 | 58 | ### Additional context ### 59 | Add any other context about the problem here. 60 | 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: FlatHub's bug tracker 4 | url: https://github.com/flathub/io.github.spacingbat3.webcord/issues 5 | about: Report issues regarding to Flatpaks (otherwise make sure it is reproducible ouside of the flatpaks). 6 | - name: Discord server 7 | url: https://discord.gg/aw7WbDMua5 8 | about: Talk, ask questions and give feedback via Discord. 9 | - name: Discussions 10 | url: https://github.com/SpacingBat3/WebCord/discussions 11 | about: Talk, ask questions and give feedback via GitHub Discussions. 12 | - name: Email 13 | url: mailto:git@spacingbat3.anonaddy.com 14 | about: Contact me via e-mail (I might respond with another e-mail address). 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project. 3 | assignees: ['SpacingBat3'] 4 | labels: ['type:feat'] 5 | body: 6 | - type: textarea 7 | id: 8 | attributes: 9 | label: Description 10 | description: Is your feature request related to a problem? Please describe. 11 | placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | - type: textarea 13 | id: 14 | attributes: 15 | label: Suggestions 16 | description: Describe the solution you'd like. 17 | placeholder: A clear and concise description of what you want to happen. 18 | - type: textarea 19 | id: 20 | attributes: 21 | label: Alternatives 22 | description: Describe alternatives you've considered. 23 | placeholder: A clear and concise description of any alternative solutions or features you've considered. 24 | - type: textarea 25 | id: context 26 | attributes: 27 | label: Additional Context 28 | description: Add any other context or screenshots about the feature request here. 29 | placeholder: | 30 | Additional information, screenshot, videos... Ex. This feature request 31 | is only related to Unix (Linux, BSD) builds. 32 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build distributables 2 | on: 3 | push: 4 | branches-ignore: 5 | - 'v*' 6 | paths-ignore: 7 | - 'docs/**' 8 | - '.github/ISSUE_TEMPLATE/**' 9 | pull_request: 10 | paths-ignore: 11 | - 'docs/**' 12 | - '.github/ISSUE_TEMPLATE/**' 13 | branches-ignore: 14 | - 'v*' 15 | 16 | env: 17 | WEBCORD_BUILD: DEVEL 18 | 19 | permissions: {} 20 | 21 | jobs: 22 | test: 23 | strategy: 24 | matrix: 25 | deps: ['latest', 'locked'] 26 | name: "Test build" 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: actions/setup-node@v4 31 | with: 32 | node-version: latest 33 | cache: npm 34 | 35 | - name: Install dependencies (NPM) 36 | run: npm ci 37 | 38 | - name: Update dependencies 39 | if: ${{ matrix.deps == 'latest' }} 40 | run: npm update 41 | 42 | - name: TSC cache 43 | uses: actions/cache@v4 44 | with: 45 | key: tsc-${{ matrix.deps }} 46 | path: | 47 | app 48 | cache/tsc.json 49 | 50 | - name: ESLint cache 51 | uses: actions/cache@v4 52 | with: 53 | key: eslint-${{ matrix.deps }} 54 | path: | 55 | cache/eslint.json 56 | 57 | - name: Run tests with ${{ matrix.deps }} dependencies 58 | run: npm test 59 | make: 60 | strategy: 61 | fail-fast: false 62 | matrix: 63 | name: ['Linux', 'Windows', 'macOS-x64', 'macOS-arm64'] 64 | build: [latest, locked] 65 | include: 66 | - runner: ubuntu-latest 67 | name: Linux 68 | arch: x64,arm64,armv7l 69 | - runner: windows-latest 70 | name: Windows 71 | arch: x64,arm64,ia32 72 | - runner: macos-latest 73 | name: macOS-x64 74 | arch: x64 #,arm64,universal 75 | - runner: macos-14 76 | name: macOS-arm64 77 | arch: arm64 78 | 79 | name: '${{ matrix.name }} (${{ matrix.build }})' 80 | runs-on: ${{ matrix.runner }} 81 | needs: ['test'] 82 | steps: 83 | - uses: actions/checkout@v4 84 | - uses: actions/setup-node@v4 85 | with: 86 | node-version: 'latest' 87 | cache: npm 88 | 89 | - name: Install dependencies (NPM) 90 | run: npm ci 91 | 92 | - name: Update dependencies 93 | if: ${{ matrix.build == 'latest' }} 94 | run: npm update 95 | 96 | - name: TSC cache 97 | uses: actions/cache@v4 98 | with: 99 | key: tsc-${{ matrix.build }} 100 | path: | 101 | app 102 | cache/tsc.json 103 | 104 | - name: Generate distributables (${{ matrix.arch }}) 105 | uses: nick-fields/retry@v2 106 | with: 107 | retry_on: error 108 | max_attempts: 3 109 | timeout_minutes: 10 110 | command: npm run make -- -a ${{ matrix.arch }} 111 | 112 | - name: Upload distributables as artifact 113 | uses: actions/upload-artifact@v4 114 | with: 115 | name: ${{ matrix.name }}_${{ matrix.build }} 116 | path: out/devel/make/* 117 | 118 | - name: Upload lockfile as artifact 119 | if: ${{ matrix.build == 'latest' }} 120 | uses: actions/upload-artifact@v4 121 | with: 122 | name: ${{ matrix.name }}-lockfile_updated 123 | path: package-lock.json -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '28 12 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /.github/workflows/njsscan.yml: -------------------------------------------------------------------------------- 1 | # This workflow integrates njsscan with GitHub's Code Scanning feature 2 | 3 | name: "NJSScan" 4 | 5 | on: 6 | push: 7 | branches: [ "master" ] 8 | pull_request: 9 | branches: [ "master" ] 10 | schedule: 11 | - cron: '28 12 * * 3' 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | njsscan: 18 | permissions: 19 | contents: read # for actions/checkout to fetch code 20 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 21 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 22 | runs-on: ubuntu-latest 23 | name: njsscan code scanning 24 | steps: 25 | - name: Checkout the code 26 | uses: actions/checkout@v3 27 | - name: nodejsscan scan 28 | id: njsscan 29 | uses: ajinabraham/njsscan-action@7237412fdd36af517e2745077cedbf9d6900d711 30 | with: 31 | args: '. --sarif --output results.sarif || true' 32 | - name: Upload njsscan report 33 | uses: github/codeql-action/upload-sarif@v2 34 | with: 35 | sarif_file: results.sarif 36 | -------------------------------------------------------------------------------- /.github/workflows/potential-duplicates.yml: -------------------------------------------------------------------------------- 1 | name: Check issues for potential duplicates 2 | on: 3 | issues: 4 | types: [opened, edited] 5 | jobs: 6 | run: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: wow-actions/potential-duplicates@v1 10 | with: 11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 12 | exclude: 'WebCord Discord Fosscord broken bug' 13 | state: all 14 | threshold: 0.6 15 | reactions: 'confused' 16 | comment: | 17 | Potential duplicates: {{#issues}} 18 | - [#{{ number }}] {{ title }} ({{ accuracy }}%) 19 | {{/issues}} 20 | -------------------------------------------------------------------------------- /.github/workflows/prerelease.yml: -------------------------------------------------------------------------------- 1 | name: Publish pre-release 2 | on: 3 | push: 4 | tags: ['*beta*'] 5 | 6 | env: 7 | WEBCORD_BUILD: DEVEL 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | publish: 14 | strategy: 15 | matrix: 16 | include: 17 | - name: Linux 18 | runner: ubuntu-latest 19 | arch: x64,arm64,armv7l 20 | - name: Windows 21 | runner: windows-latest 22 | arch: x64,arm64,ia32 23 | - name: macOS-x64 24 | runner: macos-latest 25 | arch: x64 #,arm64,universal 26 | - name: macOS-arm64 27 | runner: macos-14 28 | arch: arm64 29 | 30 | name: ${{ matrix.name }} (${{ matrix.arch }}) 31 | runs-on: ${{ matrix.runner }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: actions/setup-node@v4 35 | with: 36 | node-version: 'latest' 37 | cache: npm 38 | 39 | - name: Install dependencies (NPM) 40 | run: npm ci 41 | 42 | - name: TSC cache 43 | uses: actions/cache@v4 44 | with: 45 | key: tsc-release 46 | path: | 47 | app 48 | cache/tsc.json 49 | 50 | - name: Publish distributables (${{ matrix.arch}}) 51 | uses: nick-fields/retry@v2 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | with: 55 | retry_on: error 56 | max_attempts: ${{ matrix.name == 'Windows' && 6 || 3 }} 57 | timeout_minutes: ${{ matrix.name == 'Windows' && 15 || 10 }} 58 | command: npm run publish -- -a ${{ matrix.arch }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish release 2 | on: 3 | push: 4 | tags-ignore: ['*beta*'] 5 | 6 | env: 7 | WEBCORD_BUILD: RELEASE 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | publish: 14 | strategy: 15 | matrix: 16 | include: 17 | - name: Linux 18 | runner: ubuntu-latest 19 | arch: x64,arm64,armv7l 20 | - name: Windows 21 | runner: windows-latest 22 | arch: x64,arm64,ia32 23 | - name: macOS-x64 24 | runner: macos-latest 25 | arch: x64 #,arm64,universal 26 | - name: macOS-arm64 27 | runner: macos-14 28 | arch: arm64 29 | 30 | name: ${{ matrix.name }} (${{ matrix.arch }}) 31 | runs-on: ${{ matrix.runner }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: actions/setup-node@v4 35 | with: 36 | node-version: 'latest' 37 | cache: npm 38 | 39 | - name: Install dependencies (NPM) 40 | run: npm ci 41 | 42 | - name: TSC cache 43 | uses: actions/cache@v4 44 | with: 45 | key: tsc-prerelease 46 | path: | 47 | app 48 | cache/tsc.json 49 | 50 | - name: Publish distributables (${{ matrix.arch}}) 51 | uses: nick-fields/retry@v2 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | with: 55 | retry_on: error 56 | max_attempts: ${{ matrix.name == 'Windows' && 6 || 3 }} 57 | timeout_minutes: ${{ matrix.name == 'Windows' && 15 || 10 }} 58 | command: npm run publish -- -a ${{ matrix.arch }} 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build / packaging artifacts 2 | out/ 3 | app/ 4 | cache/ 5 | # Ignore Node package managers artifacts 6 | node_modules/ 7 | # Ignore foreign lockfiles 8 | *lock* 9 | !package-lock.json 10 | # Ignore hidden dirs with some exceptions 11 | # (these are likely used by text editors) 12 | .*/ 13 | # GitHub – workflows, templates, configs etc. 14 | !.github/ 15 | # Husky – Git hooks implementation 16 | !.husky/ 17 | .husky/_/ -------------------------------------------------------------------------------- /.husky/core.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # shellcheck shell=bash 3 | # 4 | # Core utilities for hooks 5 | # 6 | 7 | # `npkg` – finds first available Node package manager and wraps it for common 8 | # syntax so it can be used for scripts. 9 | # 10 | # **Usage:** 11 | # ```sh 12 | # npkg {add|run|test|install} [ARGS] 13 | # ``` 14 | # shellcheck disable=SC2317 15 | npkg() { 16 | local PKG SUPPORT; 17 | # A path to Node.js package manager. 18 | PKG="$(command -v npm || command -v yarn || command -v pnpm || echo false)"; 19 | # A list of supported package manager commands 20 | SUPPORT=(install test run add); 21 | _install() { 22 | case "${PKG##*/}" in 23 | "npm"|"yarn"|"pnpm") printf "install";; 24 | *) return 127;; 25 | esac 26 | } 27 | _test() { 28 | case "${PKG##*/}" in 29 | "npm"|"yarn"|"pnpm") printf "test";; 30 | *) return 127;; 31 | esac 32 | } 33 | _run() { 34 | case "${PKG##*/}" in 35 | "npm"|"yarn"|"pnpm") printf "run";; 36 | *) return 127;; 37 | esac 38 | } 39 | _add() { 40 | case "${PKG##*/}" in 41 | "npm"|"pnpm") printf "install";; 42 | "yarn") printf "add";; 43 | *) return 127;; 44 | esac 45 | } 46 | # Syntax checks. 47 | { 48 | # Unknown package manager (or lack of it). 49 | if [[ "$PKG" == "false" ]]; then 50 | echo "npkg: Couldn't find any supported Node manager!" >&2 51 | echo "npkg: Search list: 1.npm 2.yarn 3.pnpm" >&2 52 | return 200 53 | fi 54 | # Lack of first argument 55 | if [[ -z "$1" ]]; then 56 | echo "npkg: No command specified!" >&2 57 | echo "npkg: Supported commands: ${SUPPORT[*]}!" >&2 58 | return 202 59 | fi 60 | local VALID=0 61 | for cmd in "${SUPPORT[@]}"; do 62 | [[ "$1" == "$cmd" ]] && VALID=1 63 | done 64 | if [[ $VALID == 0 ]]; then 65 | echo "npkg: Unknown command: '${1}'!" >&2 66 | echo "npkg: Supported commands: ${SUPPORT[*]}!" >&2 67 | return 127 68 | fi 69 | } 70 | printf '\n%s\n\n' "npkg: Selected package manager: '${PKG##*/}'." 71 | "$PKG" "$("_$1")" "${@:2}" 72 | } 73 | 74 | # `c_json` – `JSON.parse()` wrapper for BASH. Part of `core.sh`. 75 | # 76 | # **Usage:** 77 | # ```sh 78 | # c_json .[property] PATH 79 | # ``` 80 | c_json() { 81 | local query file; 82 | if [[ "$1" == "." ]]; then 83 | query=""; 84 | else 85 | query="$1"; 86 | fi 87 | file="$(tr -d '\n' <<< "${2//'"'/'\\"'}")"; 88 | node -e "console.log(JSON.parse(\"$file\")$query);"; 89 | return $?; 90 | } 91 | 92 | # `c_svcom` – A SemVer comparassion for BASH. 93 | # 94 | # **Usage:** 95 | # ``` 96 | # c_svcom () 97 | # ``` 98 | c_svcom() { 99 | local sedrule subvr_1 subvr_2 vtype; 100 | case "$1" in 101 | maj{,or}) vtype=1 ;; 102 | min{,ior}) vtype=2 ;; 103 | patch) vtype=3 ;; 104 | *) vtype="$1" ;; 105 | esac 106 | if [[ "$vtype" -lt 1 || "$vtype" -gt 3 ]]; then return 1; fi; 107 | sedrule="s/.*\([\^<]\)\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1 \\$((vtype+1))/"; 108 | mapfile -t subvr_1 < <(sed "$sedrule" <<< "$2"); 109 | mapfile -t subvr_2 < <(sed "$sedrule" <<< "$3"); 110 | if [[ "${subvr_1[0]}" != "${subvr_2[0]}" ]]; then return 2; fi; 111 | if [[ "${subvr_1[1]}" -gt "${subvr_2[1]}" ]]; then 112 | echo 1; 113 | elif [[ "${subvr_1[1]}" -eq "${subvr_2[1]}" ]]; then 114 | echo 0; 115 | elif [[ "${subvr_1[1]}" -lt "${subvr_2[1]}" ]]; then 116 | echo -1; 117 | fi 118 | return 3; 119 | } -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S sh -e 2 | # shellcheck shell=bash 3 | 4 | if [ -z "$husky_shell_switch" ]; then 5 | readonly husky_shell_switch=1; 6 | export husky_shell_switch; 7 | # Use BASH instead of SH with Husky. 8 | # (Not all scripts are POSIX-compilant). 9 | exec bash -e "$0" "$@"; 10 | fi 11 | 12 | . "$(dirname -- "$0")/core.sh"; 13 | 14 | PKGROOT="$(dirname -- "$0")/.."; 15 | 16 | # Check metadata files. 17 | 18 | mapfile -t FILES < <(git diff --staged --name-only); 19 | lock=false; 20 | meta=false; 21 | for file in "${FILES[@]}"; do 22 | if [[ "$file" == "package-lock.json" ]]; then 23 | lock=true; 24 | elif [[ "$file" == "package.json" ]]; then 25 | meta=true; 26 | fi 27 | if [[ $lock == "true" && $meta == "true" ]]; then break; fi 28 | done 29 | if [[ "$meta" == "false" && "$lock" == "true" ]]; then 30 | echo >&2; 31 | echo "pre-commit: unnecesary-lockfile" >&2; 32 | printf ' %s\n' \ 33 | "It seems that you've tried to commit a lock file without any changes"\ 34 | "done in 'package.json'! This operation will be blocked as lockfile"\ 35 | "should not be bumped unless a development tree has changed in some way"\ 36 | "or commit is made that bumps package version and the new tag is going"\ 37 | "to be released" >&2 38 | echo >&2; 39 | exit 1; 40 | elif [[ "$meta" == "true" && "$lock" == "false" ]]; then 41 | old="$(git show HEAD:/package.json)"; 42 | new="$(cat "$PKGROOT/package.json")"; 43 | versions=( 44 | "$(c_svcomp 1 "$(c_json .version "$new")" "$(c_json .version "$old")")" 45 | "$(c_svcomp 1 "$(c_json .dependencies.electron"$new")" "$(c_json .dependencies.electron "$ld")")" 46 | ) 47 | if [[ "${versions[0]}" -eq 0 && "${versions[1]}" -eq 1 ]]; then 48 | echo >&2; 49 | echo "pre-commit: breaking-deps!" >&2; 50 | printf ' %s\n' \ 51 | "It seems that you've tried to commit a 'package.json' file in which"\ 52 | "there was made a major release bump to Electron. This change is considered" 53 | "as 'breaking' because it can announce new bugs into the application due"\ 54 | "to the API changes, Chromium/Node.js bump and potentially untested"\ 55 | "features which has been recently announced. For this reason, WebCord"\ 56 | "should be bumped to the next major version as well." >&2 57 | echo >&2; 58 | exit 2; 59 | fi 60 | fi 61 | 62 | # Run package tests (compiler+linter). 63 | 64 | npkg test; -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | lockfile-version=3 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2023 Dawid Papiewski "SpacingBat3" 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /docs/Build.md: -------------------------------------------------------------------------------- 1 | # Working with the sources 2 | 3 | When working with the WebCord, there're many tools you may have to use to be 4 | able to compile it from TypeScript to JavaScript, package it to the 5 | distributable format, run linter and so on. This section will describe the 6 | commands you may need to know to proceed with its development or packaging it 7 | yourself from its source code. 8 | 9 | > [!NOTE] 10 | > To simplify the documentation, only npm command syntax is 11 | > shown below. If you prefer using another package manager, you're free to 12 | > do so as in my philosophy you should not be limited with the set of 13 | > tools used by maintainers and have the right to choose your own. 14 | 15 | > [!WARNING] 16 | > WebCord comes with package-lock.json as a lock file format 17 | > and your preferred package manager might not be functional with it. You 18 | > still can install the latest dependencies, yet the created lockfile will 19 | > not be used to reproduce the tested environment when a dependency 20 | > breakage will occur. 21 | 22 | ## Install app dependencies. 23 | 24 | This is the first command you need to execute – without that, you won't be able 25 | to proceed with the app testing and run most of the commands listed in 26 | `package.json`. 27 | ```sh 28 | npm ci 29 | ``` 30 | 31 | This command uses `package-lock.json` which contains resolved package tree for 32 | faster package installations. While `package-lock.json` contains the dependency 33 | tree confirmed to work in the past or in case of the releases it can also be 34 | used to reproduce the builds, you might want to opt-in for the latest 35 | *non-breaking* (according to `semver` specification) dependencies that might 36 | contain bug fixes and security patches. This normally won't cause any breakages 37 | and you could still go back to the previous state with `git` if anything goes 38 | wrong. 39 | 40 | To update dependencies to the latest version available: 41 | 42 | ``` 43 | npm update 44 | ``` 45 | 46 | Be aware that `npm ci` will also install the development dependencies. **This is** 47 | **probably what you want**, as those dependencies includes all of the 48 | recommended packages required for compiling, linting and packaging WebCord. If 49 | you however want to install the production dependencies only (i.e. you want to 50 | use your own set of the tools or have installed them globally with `npm i -g`), 51 | you can use the following command instead: 52 | ``` 53 | npm i --only=prod 54 | ``` 55 | 56 | ## Compile code and run app directly (without packaging). 57 | 58 | After you have installed all required `dependencies` and `devDependencies`, you 59 | can use the following command to incrementally compile WebCord's source code 60 | from TypeScript to JavaScript: 61 | ``` 62 | npm run build 63 | ``` 64 | 65 | To both compile code and start application even before it is packaged: 66 | ``` 67 | npm start 68 | ``` 69 | 70 | ## Run linter and validate the code. 71 | 72 | While developing, you may want to check if your code quality is enough to be 73 | accepted as part of the WebCord project. To do that, you may want to both 74 | compile it (to ensure there're no compiler errors) and run linter (to check for 75 | the other issues). To do this, you can use the command below: 76 | ``` 77 | npm test 78 | ``` 79 | 80 | You can also run linter only if you don't want to compile the code: 81 | ``` 82 | npm run lint 83 | ``` 84 | 85 | ## Packaging / creating distributables. 86 | 87 | If you want to share with someone the binaries of your packaged application, or 88 | just install and/or use it without the source code and development packages, 89 | you can generate all distributable files that are valid for your platform using 90 | following command: 91 | ``` 92 | npm run make 93 | ``` 94 | 95 | You can also create a directory containing a packaged app. This directory isn't 96 | adapted for a specific distributable format, but it contains the Electron binary 97 | with the compiled application, which needs to be set up manually if you want to 98 | install it within your OS. To package an application without packing it as 99 | distributable, execute the following command: 100 | ``` 101 | npm run package 102 | ``` 103 | 104 | This will package the app for your current platform. 105 | 106 | ## Build environment variables. 107 | 108 | While making app distributables with the `npm run make` you can use some 109 | environment variables that will take effect on the application before it is 110 | packaged. See [`Flags.md`](./Flags.md#1-in-electron-forge) for the further 111 | information. -------------------------------------------------------------------------------- /docs/Contributing.md: -------------------------------------------------------------------------------- 1 | # How to contribute in the project? 2 | 3 | This file describes the ways of contributing to the project. 4 | 5 | ## How do I report a bug or request a new feature? 6 | 7 | You can do this via the application's menu bar or the tray menu. Application will 8 | then generate a link to the new GitHub issue with the pre-filled details about 9 | your operating system (you still need to describe the issue through, it doesn't 10 | automatically send a bug report for you). You can also report issues via the 11 | project's GitHub repository. 12 | 13 | When creating a GitHub issue, please take a look: 14 | 15 | - if there're any similar issues (including the closed ones); if so, try to 16 | describe your issue under that one or ask to reopen it to bring me attention; 17 | 18 | - if your issue can be reproduced within the web version – fixing Discord 19 | bugs there isn't for now the priority of this project and could cause a 20 | possible breakages on the site updates; 21 | 22 | ## How could I work within app development and create a pull request? 23 | 24 | When working with the WebCord's source code, I recommend reading the 25 | documentation about [each of the files](./Files.md) and what is their purpose in 26 | WebCord to know where you should put your code within the existing files. I'm 27 | also encouraging to read the following parts of the documentation: 28 | 29 | - [`Build.md`], to know more about current WebCord's development script syntax, 30 | including how to compile, test and package source files. 31 | 32 | - [`Flags.md`], to know a little more about the current build flags 33 | implementation. 34 | 35 | ## How to translate WebCord? 36 | 37 | Currently, WebCord has moved its translation to its [Weblate instance][weblate]. 38 | It includes the current state of the translation project, instructions and 39 | limitations. You are free to translate it *the old way* (by doing a PR), yet 40 | Weblate changes might be pulled earlier in order to avoid conflicts on Weblate 41 | side. 42 | 43 | ## Other ways of the contribution: 44 | 45 | You can also help to maintain this project by: 46 | - taking part in / answering GitHub discussions, 47 | - helping me to solve issues, 48 | - updating / working on the documentation, 49 | - reviewing the WebCord's source code or pull requests and suggesting the 50 | changes. 51 | 52 | [`Build.md`]: Build.md 53 | [`Flags.md`]: Flags.md 54 | [weblate]: https://hosted.weblate.org/projects/spacingbat3-webcord -------------------------------------------------------------------------------- /docs/Flags.md: -------------------------------------------------------------------------------- 1 | # Command line (runtime) flags 2 | 3 | WebCord is capable of parsing some Chromium flags and following 4 | application-specific flags: 5 | 6 | - **`--start-minimized` or `-m`** – start WebCord minimized in tray; 7 | useful when running WebCord at system start; 8 | 9 | - **`--version` or `-V`** – display application version and exit even before 10 | *app is ready*. 11 | 12 | - **`--help`, `-?` or `-h`** – display help information about the application. 13 | 14 | - **`--export-l10n={dir}`** – export currently loaded translations as a set of 15 | JSON files to the **`{dir}`** directory. 16 | 17 | - **`--verbose` or `-v`** – show debug messages. 18 | 19 | - **`--gpu-info=basic|complete`** – displays a raw GPU information from Chromium 20 | in form of a JavaScript object. 21 | 22 | # Build flags: 23 | 24 | ## 1. In Electron Forge 25 | 26 | While packaging the application with the Electron Forge, WebCord supports 27 | following build environment variables to set build specific flags: 28 | 29 | - `WEBCORD_BUILD={release,stable,devel}` – if set to `release` or `stable`, 30 | WebCord will be build as a stable release, with some experimental features 31 | disabled that are not meant for production build. Default build type is 32 | `devel`. 33 | 34 | - `WEBCORD_ASAR={true,false}` – if set to `false`, WebCord won't be packaged to 35 | the `asar` archive. Default is `true`. 36 | 37 | - `WEBCORD_UPDATE_NOTIFICATIONS={true,false}` – if set to `false`, notifications 38 | won't show on the new updates; this feature is meant for the package 39 | maintainers so they can disable the notifications for their users and let the 40 | package manager to handle the update notifications. 41 | 42 | - `WEBCORD_WIN32_APPID=[string]` *(Windows only)* – replaces the `ApplicationUserModelID`, used 43 | as an unique application identifier. Default is `SpacingBat3.WebCord`. You 44 | should replace it if you want to differ your build from the official ones, 45 | e.g. if you release your own WebCord package for Windows with your own 46 | patches and want to allow for coexisting it with the official WebCord 47 | executables. 48 | 49 | - `WEBCORD_FLATPAK_ID=[string]` *(Linux only)* – Used in Flatpak manifest file 50 | as an unique app identifier. For community builds it should be set to 51 | something else. Defaults to `io.github.spacingbat3.webcord` 52 | 53 | ## 2. Other tools 54 | 55 | If you're packaging the application on your own, you can create directly a 56 | `buildInfo.json` file, which is used internally by WebCord do determine the 57 | state of the build environment flags (except `asar` packaging, this is what you 58 | need to implement or configure with your own Electron packaging software). 59 | The `buildInfo.json` file should be placed in the application's root directory 60 | (i.e. next to `package.json`) and contain following properties: 61 | 62 | - `"type": "devel"/"release"` – similarly to `WEBCORD_BUILD`, this controls 63 | whenever this build is meant for production use or for development purposes. 64 | If not set, WebCord's build type will be set as `devel`. 65 | 66 | - `"commit": [hash]` – this property will save the information about the build 67 | commit; it is ignored for the `release` build type. 68 | 69 | - `AppUserModelId: [string]` *(Windows only)* – defines an 70 | `ApplicationUserModelId` of the WebCord build, this should always be present 71 | in Windows distributables. 72 | 73 | - `"features": [Object]` – this is the object controlling some features; 74 | currently it can contain these optional properties: 75 | 76 | - `"updateNotifications": true/false` – whenever to show notifications on 77 | the new releases; this does not disable the update checker to print the 78 | its current status in the console (i.e. if the version is out-of-date). -------------------------------------------------------------------------------- /docs/Privacy.md: -------------------------------------------------------------------------------- 1 | ## Privacy Policy 2 | 3 | This software **does not** share any data to its owner/maintainers – user 4 | configuration, session details and cookies are saved locally. However because 5 | this software connects to [Discord website](https://discord.com/app) as well as 6 | Fosscord instances (depending on user selection), the application may share some 7 | data or even track you depending on the WebCord's configuration. By default, 8 | WebCord tries to protect the user privacy and does not grant sensitive 9 | permissions as well as mitigate the tracing by blocking some of the unnecessary 10 | request made by the client itself. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./schemas/schemastore/package.json", 3 | "name": "webcord", 4 | "productName": "WebCord", 5 | "version": "4.11.0", 6 | "description": "A Discord made with the Electron API.", 7 | "main": "app/code/common/main.js", 8 | "scripts": { 9 | "prepare": "husky", 10 | "test": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --cache --cache-location cache/eslint.json --ext .ts .", 11 | "lint": "ESLINT_USE_FLAT_CONFIG=false eslint --cache --cache-location cache/eslint.json --ext .ts .", 12 | "build": "tsc", 13 | "compile": "tsc", 14 | "tsc": "tsc", 15 | "watch": "tsc --watch", 16 | "start": "tsc && electron .", 17 | "package": "tsc && electron-forge package", 18 | "make": "tsc && electron-forge make", 19 | "publish": "tsc && electron-forge publish", 20 | "prepack": "tsc && electron-forge package && echo 'This script will now fail to cancel creating a Node package...' && exit 1" 21 | }, 22 | "author": { 23 | "name": "SpacingBat3", 24 | "email": "git@spacingbat3.anonaddy.com", 25 | "url": "https://github.com/SpacingBat3" 26 | }, 27 | "license": "MIT", 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/SpacingBat3/WebCord" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/SpacingBat3/WebCord/issues" 34 | }, 35 | "homepage": "https://github.com/SpacingBat3/WebCord#readme", 36 | "devDependencies": { 37 | "@electron-forge/cli": "^7.1.0", 38 | "@electron-forge/maker-deb": "^7.1.0", 39 | "@electron-forge/maker-dmg": "^7.1.0", 40 | "@electron-forge/maker-flatpak": "^7.1.0", 41 | "@electron-forge/maker-rpm": "^7.1.0", 42 | "@electron-forge/maker-snap": "^7.1.0", 43 | "@electron-forge/maker-squirrel": "^7.1.0", 44 | "@electron-forge/maker-wix": "^7.1.0", 45 | "@electron-forge/maker-zip": "^7.1.0", 46 | "@electron-forge/plugin-fuses": "^7.1.0", 47 | "@electron-forge/publisher-github": "^7.1.0", 48 | "@electron/fuses": "^1.5.0", 49 | "@reforged/maker-appimage": "^5.0.0", 50 | "@stylistic/eslint-plugin": "^4.2.0", 51 | "@tsconfig/strictest": "^2.0.0", 52 | "@types/node": "^20.16.1", 53 | "@types/semver": "^7.3.9", 54 | "@types/source-map-support": "^0.5.4", 55 | "@types/ws": "^8.5.1", 56 | "@typescript-eslint/eslint-plugin": "^8.2.0", 57 | "electron": "^36.2.0", 58 | "eslint": "latest", 59 | "eslint-import-resolver-typescript": "latest", 60 | "eslint-plugin-import": "latest", 61 | "husky": "^9.0.0", 62 | "typescript": "^5.0.4" 63 | }, 64 | "dependencies": { 65 | "@spacingbat3/disconnection": "^1.0.3", 66 | "@spacingbat3/kolor": "^4.0.0", 67 | "deepmerge-ts": "^7.0.3", 68 | "dompurify": "^3.0.1", 69 | "marked": "^15.0.4", 70 | "marked-alert": "^2.1.2", 71 | "marked-gfm-heading-id": "^4.0.0", 72 | "semver": "^7.3.5", 73 | "source-map-support": "^0.5.21", 74 | "tslib": "^2.3.1", 75 | "twemoji-colr-font": "^15.0.3" 76 | }, 77 | "config": { 78 | "forge": "./app/code/build/forge.mjs" 79 | }, 80 | "engines": { 81 | "node": "^16.10.0 || ^18.10.0 || >=19.0.0", 82 | "npm": ">=8.0.0" 83 | }, 84 | "private": true 85 | } 86 | -------------------------------------------------------------------------------- /schemas/localization/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "JSON schema for configuration window (settings.json) localization files.", 4 | "definitions": { 5 | "subsection": { 6 | "description": "A subsection defining a configuration for specific functionality of the application.", 7 | "type": "object", 8 | "required": ["name","description"], 9 | "properties": { 10 | "name": { 11 | "type": "string", 12 | "description": "A short name for this subsection. Rendered in uppercase letters. Should be short, but can be little longer section name. It shouldn't end with a full stop symbol since it shouldn't be a sentence." 13 | }, 14 | "description": { 15 | "type": "string", 16 | "description": "A detailed description of this subsection. Preferably, you should include the non-breaking spaces in order to describe how breaks should occur in this text. You can also use some HTML tags for text formatting, others will be sanitized for security reasons. It should end with a full stop symbol as it should be a sententce." 17 | }, 18 | "labels": { 19 | "type": "object", 20 | "description": "A labels of input elements associated with this subsection", 21 | "additionalProperties": { 22 | "type": "string", 23 | "description": "A label for input element with the same id or name as this entry key. You can use there HTML tags for text formatting, others will be sanitized for security reasons. It should end with a full stop symbol when it is a sentence." 24 | } 25 | }, 26 | "info": { 27 | "type": "object", 28 | "description": "Description for each of the elements in \"labels\". This can be used to further explain it, to keep \"labels\" short and uncluttered.", 29 | "additionalProperties": { 30 | "type": "string", 31 | "description": "A title for input element with the same id or name as this entry key. It should end with a full stop symbol when it is a sentence." 32 | } 33 | } 34 | }, 35 | "additionalProperties": false 36 | }, 37 | "section": { 38 | "description": "One of top-level sections in rendered configuration window. Groups features by their type, e.g. features associated with the user privacy are inside the \"Privacy\" section.", 39 | "type": "object", 40 | "required": ["name"], 41 | "properties": { 42 | "name": { 43 | "type": "string", 44 | "description": "A name of this section. While there are no limitations to this property value length, it is recommended to keep it as short and concise as possible. It should not end with a full stop symbol as this property value should not be a sentence." 45 | } 46 | }, 47 | "additionalProperties": { 48 | "$ref": "#/definitions/subsection" 49 | } 50 | } 51 | }, 52 | "properties": { 53 | "$schema": { 54 | "type": "string", 55 | "description": "A path to settings.json JSON schema. DO NOT CHANGE THIS WHEN CREATING NEW LOCALIZATION FILES." 56 | } 57 | }, 58 | "additionalProperties": { 59 | "$ref": "#/definitions/section" 60 | } 61 | } -------------------------------------------------------------------------------- /schemas/schemastore/NOTICE: -------------------------------------------------------------------------------- 1 | The JSON schemas in this folder are modified version of schemas distributed at 2 | and should not be considered as a part of this 3 | software. They're distributed specifically for this software, to bring better 4 | suggestions to supported text editors for JSON files included as part of this 5 | software. 6 | 7 | I DO NOT CLAIM ANY KIND OF OWNERSHIP OF THIS FILES AND BY THAT MEANING THEY'RE 8 | DISTRIBUTED UNDER SAME CONDITIONS AS IT IS IN CASE OF SCHEMA STORE FILES. 9 | 10 | This is the copyright notice took from NOTICE file from schemastore 11 | GitHub repository: 12 | 13 | JSON Schema Store 14 | Copyright 2015-Current Mads Kristensen and Contributors 15 | 16 | The Apache license is also distributed with these schema stores as it is in 17 | original GitHub project. -------------------------------------------------------------------------------- /sources/assets/icons/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpacingBat3/WebCord/085fe5f5030d6f24d9c553fcc90039a5cf916257/sources/assets/icons/app.icns -------------------------------------------------------------------------------- /sources/assets/icons/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpacingBat3/WebCord/085fe5f5030d6f24d9c553fcc90039a5cf916257/sources/assets/icons/app.ico -------------------------------------------------------------------------------- /sources/assets/icons/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpacingBat3/WebCord/085fe5f5030d6f24d9c553fcc90039a5cf916257/sources/assets/icons/app.png -------------------------------------------------------------------------------- /sources/assets/icons/symbols/checkmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/assets/icons/symbols/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sources/assets/icons/symbols/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sources/assets/icons/symbols/speaker.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sources/assets/icons/tray-ping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpacingBat3/WebCord/085fe5f5030d6f24d9c553fcc90039a5cf916257/sources/assets/icons/tray-ping.png -------------------------------------------------------------------------------- /sources/assets/icons/tray-unread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpacingBat3/WebCord/085fe5f5030d6f24d9c553fcc90039a5cf916257/sources/assets/icons/tray-unread.png -------------------------------------------------------------------------------- /sources/assets/icons/tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpacingBat3/WebCord/085fe5f5030d6f24d9c553fcc90039a5cf916257/sources/assets/icons/tray.png -------------------------------------------------------------------------------- /sources/assets/web/css/capturer.css: -------------------------------------------------------------------------------- 1 | @import "./global.css"; 2 | 3 | html, body { 4 | background-color: transparent; 5 | } 6 | 7 | #capturer-selection { 8 | position: fixed; 9 | top: 0; 10 | left: 0; 11 | width: 100%; 12 | height: 100vh; 13 | background: #000a; 14 | color: var(--color-font-normal); 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | } 19 | 20 | #capturer-scroller { 21 | width: 100%; 22 | max-height: 100vh; 23 | overflow-y: auto; 24 | } 25 | 26 | #capturer-list { 27 | max-width: calc(100% - 100px); 28 | margin: 50px; 29 | padding: 0; 30 | display: flex; 31 | flex-wrap: wrap; 32 | list-style: none; 33 | overflow: hidden; 34 | justify-content: center; 35 | } 36 | 37 | .capturer-item { 38 | display: flex; 39 | margin: 4px; 40 | } 41 | 42 | .capturer-button { 43 | cursor: pointer; 44 | display: flex; 45 | flex-direction: column; 46 | align-items: stretch; 47 | width: 145px; 48 | margin: 0; 49 | border: 0; 50 | border-radius: 3px; 51 | padding: 4px; 52 | color: #FFFFFF; 53 | background: var(--color-input-bg); 54 | text-align: left; 55 | transition: background-color .15s, box-shadow .15s; 56 | } 57 | 58 | .capturer-button:hover, 59 | .capturer-button:focus { 60 | background: var(--color-accent); 61 | } 62 | .capturer-thumbnail { 63 | width: 100%; 64 | height: 81px; 65 | object-fit: cover; 66 | } 67 | .capturer-label-container { 68 | margin: 6px 0 6px; 69 | display: flex; 70 | flex-shrink: 0; 71 | flex-grow: 0; 72 | } 73 | 74 | .capturer-label-icon { 75 | width: 16px; 76 | padding-right: 4px; 77 | } 78 | 79 | .capturer-label { 80 | color: #dcddde; 81 | white-space: nowrap; 82 | text-overflow: ellipsis; 83 | overflow: hidden; 84 | } 85 | 86 | #capturer-buttons { 87 | position: fixed; 88 | display: flex; 89 | flex-direction: column; 90 | right: 15px; 91 | top: 50%; 92 | transform: translateY(-50%); 93 | } 94 | 95 | #capturer-buttons > * { 96 | display: flex; 97 | cursor: pointer; 98 | justify-content: center; 99 | align-items: center; 100 | background-color: var(--color-input-bg); 101 | color: #dcddde; 102 | width: 32px; 103 | height: 32px; 104 | transition: background-color .15s; 105 | border: transparent; 106 | border-radius: 3px; 107 | box-shadow: none; 108 | margin: 2px 0; 109 | } 110 | 111 | #capturer-buttons > * > img { 112 | margin: 0; 113 | padding: 0; 114 | width: 14px; 115 | } 116 | 117 | #capturer-buttons > *:hover { 118 | background-color: #678; 119 | } 120 | 121 | #capturer-buttons button#capturer-close:active { 122 | background-color: #c24242; 123 | } 124 | 125 | input[type="checkbox"]#capturer-sound { 126 | appearance: none; 127 | flex-shrink: 0; 128 | background-image: url("../../icons/symbols/speaker.svg"); 129 | background-repeat: no-repeat; 130 | background-position: center; 131 | background-size: 16px; 132 | } 133 | 134 | #capturer-sound:checked { 135 | background-color: var(--color-accent); 136 | } 137 | 138 | #capturer-buttons > *:disabled { 139 | background-color: var(--color-bg-secondary); 140 | cursor: not-allowed; 141 | } -------------------------------------------------------------------------------- /sources/assets/web/css/discord.css: -------------------------------------------------------------------------------- 1 | /* Animate side bar on resize. */ 2 | div[class^=sidebar_] 3 | div[class^=sidebarList_] { 4 | transition: width .1s cubic-bezier(0.4, 0, 0.2, 1); 5 | } 6 | 7 | /* Hide Discord elements. */ 8 | div[class^=scroller_] > div[class^=listItem_]:nth-last-child(2) > *, 9 | div[class^=scroller_] > div[class^=listItem_]:nth-last-child(3) > *, 10 | div[class*=" scroller_"] > div[class^="listItem_"]:nth-last-child(2) { 11 | display: none !important; 12 | } 13 | 14 | /* Assign 'rainbow' color to my nickname! */ 15 | img[src*="423864076095979522"] ~ :is(h1,h2,h3,h4,h5,h6) > span > span { 16 | animation: rainbow linear 20s infinite; 17 | } 18 | 19 | :root { 20 | --neon-shadow: 0 0 3px; 21 | } 22 | 23 | .theme-dark { 24 | --rb: 75% 25 | } 26 | 27 | .theme-light { 28 | --rb: 35% 29 | } 30 | 31 | @keyframes rainbow { 32 | 0% { 33 | color: hsl(0, 100%, var(--rb)); 34 | text-shadow: var(--neon-shadow) hsl(0, 100%, var(--rb)); 35 | } 36 | 15% { 37 | color: hsl(39, 100%, calc(var(--rb) - 5%)); 38 | text-shadow: var(--neon-shadow) hsl(39, 100%, calc(var(--rb) - 5%)); 39 | } 40 | 30% { 41 | color: hsl(60, 100%, calc(var(--rb) - 10%)); 42 | text-shadow: var(--neon-shadow) hsl(60, 100%, calc(var(--rb) - 10%)); 43 | } 44 | 45% { 45 | color: hsl(120, 100%, calc(var(--rb) - 5%)); 46 | text-shadow: var(--neon-shadow) hsl(120, 100%, calc(var(--rb) - 5%)); 47 | } 48 | 60% { 49 | color: hsl(240, 100%, calc(var(--rb) + 6%)); 50 | text-shadow: var(--neon-shadow) hsl(240, 100%, calc(var(--rb) + 6%)); 51 | } 52 | 75% { 53 | color: hsl(275, 100%, calc(var(--rb) + 4%)); 54 | text-shadow: var(--neon-shadow) hsl(275, 100%, calc(var(--rb) + 4%)); 55 | } 56 | 90% { 57 | color: hsl(300, 100%, calc(var(--rb) + 2%)); 58 | text-shadow: var(--neon-shadow) hsl(300, 100%, calc(var(--rb) + 2%)); 59 | } 60 | 100% { 61 | color: hsl(360, 100%, var(--rb)); 62 | text-shadow: var(--neon-shadow) hsl(360, 100%, var(--rb)); 63 | } 64 | } 65 | 66 | div[class^=bar_] { 67 | height: 0px; 68 | } 69 | 70 | /* Hide "help" button. */ 71 | div[class^=toolbar_] > a { 72 | display: none; 73 | } 74 | 75 | /* Fix popup position going negative. Also sets fixed toolbar popup position. */ 76 | div[id^=popout_]:is([style*="; right: -"],[style*="; top: 44px;"]) { 77 | right: 16px !important; 78 | } 79 | 80 | /* Limit elements width to not take space above the screen width. */ 81 | div[class^=layerContainer_] :not([class*=Tooltip]) { 82 | max-width: 100vw; 83 | } 84 | 85 | /* Mobile interface tweaks */ 86 | @media screen and (max-width: 480px) { 87 | div[class^=channelTextArea_] > div[class^=scrollableContainer_] > 88 | div[class^=inner_] > div[class^=buttons_] > :not(:nth-last-child(2)) { 89 | display: none; 90 | } 91 | /* Hide search input if the screen is too small to display toolbar right.*/ 92 | section[class^=title_] > div[class^=toolbar_] > div[class^=search_] { 93 | display: none; 94 | } 95 | /* Fix popup position being to far from the right/left. */ 96 | div[id^=popout_][style] { 97 | right: 0px !important; 98 | left: unset !important; 99 | } 100 | /* Fix size of some popups take slightly higher width than they should be. */ 101 | div[role=dialog] div[class^=header_] { 102 | box-sizing: border-box; 103 | } 104 | } -------------------------------------------------------------------------------- /sources/assets/web/css/fonts.css: -------------------------------------------------------------------------------- 1 | /** Twemoji **/ 2 | @import url('../../../../node_modules/twemoji-colr-font/twemoji.css'); 3 | 4 | :root { 5 | --font-primary: sans-serif, Twemoji; 6 | --font-display: sans-serif, Twemoji; 7 | --font-code: monospace, Twemoji; 8 | --font-headline: var(--font-display); 9 | } -------------------------------------------------------------------------------- /sources/assets/web/css/global.css: -------------------------------------------------------------------------------- 1 | @import "./fonts.css"; 2 | 3 | /* A CSS that imitates the Discord's style, with some minior tweaks. */ 4 | 5 | :root { 6 | --color-accent: #4185d3; 7 | --color-accent-alt: #287baf; 8 | --color-accent-light: 140, 202, 255; 9 | --color-bg-primary: #36393F; 10 | --color-bg-secondary: #2F3136; 11 | --color-bg-border: #1B1C1F; 12 | --color-font-settings: #8e9297; 13 | --color-font-code: #b9bbbe; 14 | --color-font-normal: #dcddde; 15 | --color-font-header: #fff; 16 | --color-link: #00A3E3; 17 | --color-input-bg: #484C52; 18 | --scrollbar-width: 7px; 19 | --scrollbar-thumb: #202225; 20 | --color-alert-info: 83, 163, 248; 21 | --color-alert-warn: 248, 197, 29; 22 | } 23 | 24 | html { 25 | margin: 0 10px; 26 | user-select: none; 27 | font-family: var(--font-primary); 28 | background-color: var(--color-bg-primary); 29 | color: var(--color-font-normal); 30 | line-height: 1.5; 31 | } 32 | 33 | h1, h2, h3, h4, h5, h6 { 34 | color: var(--color-font-header); 35 | font-family: var(--font-display); 36 | margin-top: 1em; 37 | margin-bottom: 0.5em; 38 | } 39 | 40 | ::-webkit-scrollbar { 41 | width: var(--scrollbar-width); 42 | height: var(--scrollbar-width); 43 | background-color: transparent; 44 | border-radius: 25px; 45 | } 46 | 47 | ::-webkit-scrollbar-thumb { 48 | display: none; 49 | background-color: var(--scrollbar-thumb); 50 | border-radius: 25px; 51 | box-sizing: border-box; 52 | border: solid transparent 2px; 53 | } 54 | 55 | :hover > ::-webkit-scrollbar-thumb { 56 | display: unset; 57 | } 58 | 59 | ::-webkit-scrollbar-thumb:active { 60 | background-color: var(--color-bg-border); 61 | } 62 | 63 | a { 64 | color: var(--color-link); 65 | text-decoration: unset; 66 | } 67 | 68 | a:hover { 69 | text-decoration: underline; 70 | } 71 | 72 | table { 73 | border-collapse: collapse; 74 | border-spacing: 0; 75 | margin-bottom: 1em; 76 | } 77 | 78 | td, th { 79 | padding: 8px; 80 | } 81 | 82 | tr:nth-child(odd) { 83 | background-color: var(--color-bg-secondary); 84 | } 85 | 86 | th { 87 | color: var(--color-font-header); 88 | background-color: var(--color-accent); 89 | } 90 | 91 | html :not(pre) > code { 92 | font-size: 0.8em; 93 | padding: 4px; 94 | margin-top: 0px; 95 | background-color: var(--color-bg-secondary); 96 | border-radius: 5px; 97 | } 98 | 99 | code { 100 | font-family: var(--font-code); 101 | font-size: 1em; 102 | } 103 | 104 | kbd { 105 | position: relative; 106 | font-size: 1em; 107 | background-color: var(--color-input-bg); 108 | color: var(--color-font-header); 109 | font-weight: bold; 110 | text-transform: uppercase; 111 | border-radius: 3px; 112 | box-shadow: var(--color-bg-border) 0px 4px; 113 | border: 1px solid transparent; 114 | padding: 2px 6px; 115 | margin-right: 2px; 116 | margin-left: 2px; 117 | top: -1px; 118 | } 119 | 120 | kbd:active { 121 | border-color: var(--color-bg-border); 122 | box-shadow: inset var(--color-bg-primary) 0px -2px; 123 | color: var(--color-font-normal); 124 | top: 0px; 125 | padding-bottom: 4px; 126 | } 127 | 128 | pre { 129 | display: block; 130 | border: 1px solid var(--color-bg-border); 131 | padding: 16px 12px; 132 | background-color: var(--color-bg-secondary); 133 | color: var(--color-font-code); 134 | font-family: var(--font-code); 135 | overflow: auto; 136 | border-radius: 15px; 137 | font-size: 10pt; 138 | } 139 | 140 | li { 141 | margin: 0.5em 0em; 142 | } 143 | 144 | a, img { 145 | user-select: none; 146 | -webkit-user-drag: none; 147 | -webkit-user-select: none; 148 | } 149 | 150 | blockquote { 151 | margin-inline-start: 30px; 152 | padding-left: 10px; 153 | border-left: 4px solid var(--color-input-bg); 154 | color: var(--color-font-settings); 155 | } 156 | 157 | hr { 158 | color: var(--color-input-bg); 159 | background-color: var(--color-input-bg); 160 | border: solid 2px var(--color-input-bg); 161 | } -------------------------------------------------------------------------------- /sources/assets/web/css/load.css: -------------------------------------------------------------------------------- 1 | @import "./global.css"; 2 | 3 | :root { 4 | --content-width: 300px; 5 | } 6 | 7 | html, body { 8 | margin: 0; 9 | background-color: #36393F; 10 | height: 100%; 11 | } 12 | 13 | body, div.aura { 14 | text-align: center; 15 | display: flex; 16 | margin: 0px; 17 | align-items: center; 18 | justify-content: center; 19 | flex-direction: column; 20 | } 21 | 22 | h1 { 23 | margin-top: 0.5em; 24 | } 25 | 26 | .aura-content { 27 | width: var(--content-width); 28 | margin-left: calc(50vw - (var(--content-width)/2)); 29 | margin-right: calc(50vw - (var(--content-width)/2)); 30 | overflow: hidden; 31 | white-space: normal; 32 | } 33 | 34 | .aura { 35 | height: 100%; 36 | width: 100%; 37 | background-color: var(--color-accent); 38 | animation-name: aura; 39 | animation-duration: 1s; 40 | animation-timing-function: ease-in-out; 41 | animation-fill-mode: forwards; 42 | white-space: nowrap; 43 | overflow: hidden; 44 | text-overflow: clip; 45 | } 46 | 47 | @keyframes aura { 48 | 0% {width: 0%} 49 | 10% {width: 0%;background-color: var(--color-accent)} 50 | 100% {width: 100%;background-color: transparent} 51 | } -------------------------------------------------------------------------------- /sources/assets/web/css/markdown.css: -------------------------------------------------------------------------------- 1 | @import "./global.css"; 2 | 3 | :root { 4 | --header-height: 36px; 5 | } 6 | 7 | p, li, div { 8 | font-size: 11pt; 9 | } 10 | 11 | img { 12 | max-height: 256px; 13 | } 14 | 15 | code, pre { 16 | font-size: 9pt; 17 | } 18 | 19 | sup { 20 | font-size: 8pt; 21 | } 22 | 23 | h1, h2, h3, h4, h5, h6 { 24 | color: var(--color-font-header); 25 | font-family: var(--font-display); 26 | margin-top: 1em; 27 | margin-bottom: 0.5em; 28 | scroll-margin-top: 0.5em; 29 | } 30 | 31 | h1, h2 { 32 | border-bottom: 2px solid var(--color-input-bg); 33 | padding-bottom: 5px; 34 | } 35 | 36 | h1 { 37 | margin-top: 0; 38 | } 39 | 40 | h1#webcord { 41 | font-size: xx-large; 42 | } 43 | 44 | #markdown-body > h1:first-child, 45 | #markdown-body > h2:first-child, 46 | #markdown-body > h3:first-child, 47 | #markdown-body > h4:first-child, 48 | #markdown-body > h5:first-child, 49 | #markdown-body > h6:first-child { 50 | margin-top: 0.5em; 51 | } 52 | 53 | html { 54 | width: 100vw; 55 | height: 100vh; 56 | } 57 | 58 | body { 59 | width: 100%; 60 | height: 100%; 61 | } 62 | 63 | html, body { 64 | margin: 0px; 65 | } 66 | 67 | article { 68 | width: 100vw; 69 | margin: 0px; 70 | overflow: auto; 71 | position: absolute; 72 | top: var(--header-height); 73 | bottom: 0; 74 | } 75 | 76 | #markdown-body { 77 | margin: 0px 16px; 78 | padding-top: 1em; 79 | } 80 | 81 | #markdown-header { 82 | padding-right: 24px; 83 | background-color: var(--color-bg-primary); 84 | border-bottom: 2px solid var(--color-bg-secondary); 85 | display: flex; 86 | justify-content: center; 87 | align-items: center; 88 | position: sticky; 89 | top: 0; 90 | height: var(--header-height); 91 | } 92 | 93 | #menu-hamburger { 94 | margin: 0px 5px; 95 | padding: 4px; 96 | width: 22px; 97 | height: 22px; 98 | border-radius: 8px; 99 | transition: background-color 0.5s ease-in-out; 100 | } 101 | 102 | #markdown-header p { 103 | font-family: var(--font-primary); 104 | color: var(--color-font-header); 105 | font-size: 16pt; 106 | font-weight: bold; 107 | } 108 | 109 | #menu-hamburger:hover { 110 | cursor: pointer; 111 | background-color: #fff3; 112 | transition: background-color 0.5s ease-in-out; 113 | } 114 | 115 | .markdown-alert { 116 | border-style: solid; 117 | border-width: 4px; 118 | border-radius: 16px; 119 | padding: 4px 16px; 120 | margin-bottom: 1em; 121 | } 122 | 123 | .markdown-alert-note { 124 | background-color: rgba(var(--color-alert-info), 15%); 125 | border-color: rgb(var(--color-alert-info)); 126 | } 127 | 128 | .markdown-alert-note .markdown-alert-title { 129 | color: rgb(var(--color-alert-info)); 130 | fill: rgb(var(--color-alert-info)); 131 | } 132 | 133 | .markdown-alert-warning { 134 | background-color: rgba(var(--color-alert-warn), 20%); 135 | border-color: rgb(var(--color-alert-warn)); 136 | 137 | } 138 | 139 | .markdown-alert-warning .markdown-alert-title { 140 | color: rgb(var(--color-alert-warn)); 141 | fill: rgb(var(--color-alert-warn)); 142 | } 143 | 144 | 145 | .markdown-alert-title { 146 | font-weight: bold; 147 | display: flex; 148 | align-items: center; 149 | } 150 | 151 | .markdown-alert .octicon { 152 | margin-right: 4px; 153 | } 154 | 155 | table.alert-info > tbody > tr:only-child { 156 | background-color: rgba(var(--color-alert-info), 20%); 157 | border-color: rgb(var(--color-alert-info)); 158 | } 159 | 160 | table.alert-warn > tbody > tr:only-child { 161 | background-color: rgba(var(--color-alert-warn), 20%); 162 | border-color: rgb(var(--color-alert-warn)); 163 | } -------------------------------------------------------------------------------- /sources/assets/web/css/settings.css: -------------------------------------------------------------------------------- 1 | @import "./global.css"; 2 | 3 | h1 { 4 | font-size: 1.6em; 5 | } 6 | 7 | h2 { 8 | margin-left: 15px; 9 | font-size: 0.8em; 10 | text-transform: uppercase; 11 | 12 | } 13 | 14 | p.description { 15 | font-size: 0.8em; 16 | margin: 0 15px 1em 15px; 17 | color: var(--color-font-settings); 18 | } 19 | 20 | form { 21 | display: flex; 22 | overflow-x: hidden; 23 | flex-direction: column; 24 | margin: 0 40px 2em 40px; 25 | padding: 10px 15px; 26 | background-color: var(--color-bg-secondary); 27 | font-size: 0.8em; 28 | border-radius: 5px; 29 | } 30 | 31 | fieldset { 32 | display: flex; 33 | margin: 0.5em 0px; 34 | padding: 0; 35 | border: none; 36 | } 37 | 38 | label { 39 | margin-top: 0.15em; 40 | font-size: 1em; 41 | } 42 | 43 | label.disabled { 44 | color: var(--color-font-settings); 45 | } 46 | 47 | input, label { 48 | cursor: pointer; 49 | } 50 | 51 | input:disabled, label.disabled { 52 | cursor: default; 53 | } 54 | 55 | input[type="checkbox"], input[type="radio"] { 56 | flex-shrink: 0; 57 | margin: 0 1em; 58 | height: 1.5em; 59 | width: 1.5em; 60 | border-radius: 5px; 61 | appearance: none; 62 | background-color: var(--color-input-bg); 63 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); 64 | border: solid #0000 4px; 65 | } 66 | 67 | input[type="radio"] { 68 | border-radius: 100%; 69 | background-color: var(--color-bg-secondary); 70 | border: solid var(--color-bg-secondary) 4px; 71 | outline: none; 72 | box-shadow: 0px 0px 0px 2px var(--color-font-normal); 73 | 74 | } 75 | 76 | input[type="checkbox"] { 77 | border-color: var(--color-input-bg); 78 | } 79 | 80 | input[type="radio"]:disabled { 81 | outline-color: var(--color-font-settings); 82 | } 83 | 84 | input[type="checkbox"]:checked { 85 | background-color: var(--color-accent); 86 | background-image: url(../../icons/symbols/checkmark.svg); 87 | background-repeat: no-repeat; 88 | background-position: center; 89 | background-size: contain; 90 | border-color: var(--color-accent); 91 | } 92 | 93 | input[type="radio"]:checked { 94 | background-color: rgb(var(--color-accent-light)); 95 | outline-color: var(--color-font-header) 96 | 97 | } 98 | 99 | code { 100 | background-color: var(--color-bg-primary) !important; 101 | font-size: 0.9em !important; 102 | } -------------------------------------------------------------------------------- /sources/assets/web/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 21 | 22 | 23 | 35 | 36 | -------------------------------------------------------------------------------- /sources/assets/web/html/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 23 | 24 |
25 |
26 |
27 |
28 | 29 |

30 |

31 |

32 |

33 |
34 |
35 |
36 |

Electron / Chromium / Node

37 |

38 |
39 | 40 |
41 |

42 |

43 |
44 |
45 |

46 |

47 |
48 |
49 |
50 |
51 |
52 |

53 |

54 | 55 |
56 |

57 |

58 |
59 |
60 | 100 | 101 | -------------------------------------------------------------------------------- /sources/assets/web/html/capturer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Screen Share 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 | 16 | 19 |
20 |
21 |
22 | 23 | -------------------------------------------------------------------------------- /sources/assets/web/html/docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sources/assets/web/html/load.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 |

WebCord

14 |

15 |
16 |
17 | 28 | 29 | -------------------------------------------------------------------------------- /sources/assets/web/html/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sources/assets/web/json/motd.json: -------------------------------------------------------------------------------- 1 | { 2 | "en": [ 3 | "Free as in freedom! 🗽️", 4 | "Protects your privacy! 🛡️", 5 | "Without unnecessary trackers! 🕵️", 6 | "Still ARM-friendly! ❤️", 7 | "With special care about security. 🔐️", 8 | "It's not written in Scratch for sure! 😹️", 9 | "Thank you for your trust! 👍️", 10 | "Whoa, it's still running!? 🤯️", 11 | "It didn't crash this time! 🎉️", 12 | "Your best collection of MEMEs. 🙃️", 13 | "Do not use for evil purposes! 😈️", 14 | "Somehow usable on PinePhones. 📱️", 15 | "Designed without requiring many dependencies! 🪶️", 16 | "Runs almost everywhere! 🥔️", 17 | "A next-gen MEME generator! 💩️", 18 | "Made in Poland! 🇵🇱️", 19 | "Definitely used by hackers! 😄️", 20 | "Focused on code quality and strict type checking. 🥇️", 21 | "The best Discord client according to your choice. 😉️", 22 | "Press ALTF4 to get free Nitro subscription. 😃️", 23 | "Hello World! 🌍️", 24 | "Please read FAQ before asking a question! 📄️", 25 | "Congratulations, you have found an easter egg. 🥚️" 26 | ], 27 | "pl": [ 28 | "Wolny i otwarty! 🗽️", 29 | "Chroni twoją prywatność! 🛡️", 30 | "Bez zbędnego śledzenia! 🕵️", 31 | "Wciąż przyjazny dla urządzeń ARM! ❤️", 32 | "Z troską o bezpieczeństwo kodu! 🔐️", 33 | "Na pewno nie został napisany w Scratch'u! 😹️", 34 | "Stworzony w Polsce! 🇵🇱️", 35 | "Ej, to wciąż działa!? 🤯️", 36 | "Tym razem nie wystąpił błąd w aplikacji! 🎉️", 37 | "Twoja najlepsza kolekcja MEM-ów. 🙃️", 38 | "Nie wykorzystywać do czynienia zła! 😈️", 39 | "Zaprojektowany bez wielu zależności! 🪶️", 40 | "Działa prawie wszędzie! 🥔️", 41 | "Generator MEM-ów następnej generacji! 💩️", 42 | "Definitywnie wykorzystywany przez hakerów! 😄️", 43 | "Skupiony na jakości kodu i sprawdzaniu typów. 🥇️", 44 | "Najlepszy klient Discord'a względem Twojego wyboru. 😉️", 45 | "Naciśnij ALTF4, aby uzyskać darmową subskrybcję Nitro. 😃️", 46 | "Witaj, świecie! 🌍️", 47 | "Proszę o przeczytanie FAQ przed zadaniem pytania! 📄️", 48 | "Gratulacje, znalazłeś easter egg. 🥚️" 49 | ], 50 | "tr": [ 51 | "Özgür ve açık! 🗽️", 52 | "Gizliliğinizi korur! 🛡️", 53 | "Gereksiz izleyiciler olmadan! 🕵️", 54 | "Hala ARM dostu! ❤️", 55 | "Güvenlik konusunda özel bir itina ile. 🔐️", 56 | "Tabii ki de Scratch'te yazılmadı! 😹️", 57 | "Güveniniz için teşekkürler! 👍️", 58 | "Vay canına, hala çalışıyor mu!? 🤯️", 59 | "Bu sefer çökmedi! 🎉️", 60 | "En iyi MEME koleksiyonunuz. 🙃️", 61 | "Kötü amaçlar için kullanmayın! 😈️", 62 | "Bir şekilde PinePhone'larda kullanılabiliyor. 📱️", 63 | "Bir sürü bağımlılık gerektirmeyecek şekilde tasarlandı! 🪶️", 64 | "Neredeyse her yerde çalışıyor! 🥔️", 65 | "İleri düzey bir meme üreticisi! 💩️", 66 | "Polonya'da yapıldı! 🇵🇱️", 67 | "Kesinlikle hackerlar tarafından kullanıldı! 😄️", 68 | "Kod kalitesi ve katı yazım denetimi üzerine yoğunlaşıldı. 🥇️", 69 | "Seçiminize göre en iyi Discord istemcisi. 😉️", 70 | "Ücretsiz Nitro aboneliği için ALTF4 basın. 😃️", 71 | "Merhaba Dünya! 🌍️", 72 | "Lütfen soru sormadan önce FAQ okuyun! 📄️", 73 | "Tebrikler, bir easter egg buldun. 🥚️" 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /sources/code/common/modules/agent.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Fake UserAgent generator (userAgent.js) 3 | */ 4 | 5 | import {release} from "os"; 6 | 7 | interface AgentReplace { 8 | platform: string; 9 | version: string; 10 | arch: string; 11 | device?: string|undefined; 12 | } 13 | 14 | const agentArchMap = Object.freeze({ 15 | arm64: "aarch64", 16 | arm: "armv7", 17 | ia32: "x86", 18 | x64: "x86_64" 19 | } as const); 20 | 21 | const toAgentCase = (s:string) => (s.slice(0,1).toUpperCase()+s.slice(1).toLowerCase()) 22 | .replace("bsd","BSD") 23 | .replace("os","OS"); 24 | 25 | /** 26 | * Generates fake Chrome/Chromium user agent string to use instead Electron ones. 27 | * 28 | * This way, pages identifies Electron client as regular Chromium browser. 29 | * 30 | * To make it even harder to detect, it even uses current operating system version in 31 | * the user agent string (via `process.getSystemVersion()` in Electron API). 32 | * 33 | * @param chromeVersion Chrome/Chromium version string to use. 34 | * @param mobile Whenever user-agent should be for mobile devices. 35 | * @param replace Generate user-agent from provided `replace` data. 36 | * @returns Fake Chrome/Chromium user agent string. 37 | */ 38 | export function getUserAgent(chromeVersion: string, mobile?: boolean, replace?: AgentReplace) { 39 | const userAgentPlatform = replace?.platform ?? process.platform; 40 | const osVersion = replace?.version ?? (typeof process.getSystemVersion === "function" ? 41 | process.getSystemVersion() : 42 | (userAgentPlatform === "darwin" ? "13.5.2" : release()) 43 | ); 44 | const device = replace?.device !== undefined ? `; ${replace.device}` as const : ""; 45 | const mobileAgent = (mobile??false) ? " Mobile" : ""; 46 | switch (userAgentPlatform as NodeJS.Platform) { 47 | case "darwin": 48 | return `Mozilla/5.0 (Macintosh; Intel Mac OS X ${osVersion.replace(".","_")}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion}${mobileAgent} Safari/537.36` as const; 49 | case "win32": { 50 | const wow64 = (replace?.arch ?? process.arch).endsWith("64") ? "Win64; x64" : "Win32"; 51 | return `Mozilla/5.0 (Windows NT ${osVersion}; ${wow64}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion}${mobileAgent} Safari/537.36` as const; 52 | } 53 | case "android": 54 | return `Mozilla/5.0 (Linux; Android ${osVersion}${device}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion}${mobileAgent} Safari/537.36` as const; 55 | default: { 56 | const arch = ( 57 | replace?.arch !== undefined && replace.arch in agentArchMap ? 58 | agentArchMap[replace.arch as keyof typeof agentArchMap] : 59 | replace?.arch 60 | ) ?? ( 61 | process.arch in agentArchMap ? 62 | agentArchMap[process.arch as keyof typeof agentArchMap] : 63 | process.arch 64 | ); 65 | return `Mozilla/5.0 (X11; ${toAgentCase(userAgentPlatform)} ${arch}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion}${mobileAgent} Safari/537.36` as const; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /sources/code/common/modules/client.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Declarations used between multiple files (main scripts only) 3 | */ 4 | import { nativeImage } from "electron/common"; 5 | import { getAppPath, getName } from "./electron"; 6 | import { resolve } from "path"; 7 | import { BuildInfo, typeMerge, isPartialBuildInfo } from "../global"; 8 | import packageJson, { Person } from "./package"; 9 | import { readFileSync } from "fs"; 10 | 11 | /** Icon names used in WebCord */ 12 | const enum Icon { 13 | App = "app", 14 | Tray = "tray", 15 | TrayUnread = "tray-unread", 16 | TrayPing = "tray-ping" 17 | } 18 | 19 | /** Generates `NativeIcon` which size is prefered for a given platform. */ 20 | function generateIcon(icon: Icon) { 21 | const basePath = resolve(getAppPath(), "sources/assets/icons", icon)+"."; 22 | switch(icon) { 23 | case Icon.App: 24 | return nativeImage.createFromPath(basePath+(process.platform === "win32" ? "ico" : "png")); 25 | case Icon.Tray: 26 | case Icon.TrayUnread: 27 | case Icon.TrayPing: { 28 | const image = nativeImage.createFromPath(basePath+"png"); 29 | if(process.platform !== "win32" && process.platform !== "darwin") 30 | return image; 31 | const newImage = nativeImage.createEmpty(); 32 | let sizes:[scaleFactor: number,height: number][]; 33 | if(process.platform === "win32") 34 | sizes = [[1,16],[1.5,24],[2,32]]; 35 | else 36 | sizes = [[1,22],[1.5,33],[2,44]]; 37 | sizes.forEach(element => newImage.addRepresentation({ 38 | scaleFactor: element[0], 39 | buffer: image.resize({height:element[1]}).toPNG(), 40 | })); 41 | return newImage; 42 | } 43 | default: 44 | throw new TypeError(`Invalid icon file name '${String(icon)}'.`); 45 | } 46 | } 47 | 48 | export const defaultBuildInfo = Object.freeze({ 49 | type: "devel", 50 | ...(process.platform === "win32" ? {AppUserModelId: "SpacingBat3.WebCord"} : {}), 51 | features: { 52 | updateNotifications: false 53 | } 54 | } as const satisfies Readonly); 55 | 56 | /** 57 | * Resolves the `buildInfo` using the file (for compatibility reasons and sake 58 | * of simplicity for the packagers, it assumes it is a partial file – any values 59 | * can be ommited, but the config itself still needs to be of valid types) and 60 | * default values. */ 61 | export function getBuildInfo(): Readonly { 62 | try { 63 | const data = readFileSync(resolve(getAppPath(), "buildInfo.json")); 64 | const buildInfo:unknown = JSON.parse(data.toString()); 65 | if(isPartialBuildInfo(buildInfo)) 66 | return Object.freeze(typeMerge(defaultBuildInfo, {}, buildInfo) as BuildInfo); 67 | else 68 | return defaultBuildInfo; 69 | } catch { 70 | return defaultBuildInfo; 71 | } 72 | } 73 | 74 | /** Basic application details. */ 75 | export const appInfo = Object.freeze({ 76 | /** Application repository details */ 77 | repository: { 78 | /** Repository indentifier in format `author/name`. */ 79 | name: new Person(packageJson.data.author ?? "").name + "/" + getName(), 80 | /** Web service on which app repository is published. */ 81 | provider: "github.com" 82 | }, 83 | icons: { 84 | app: generateIcon(Icon.App), 85 | tray: { 86 | default: generateIcon(Icon.Tray), 87 | unread: generateIcon(Icon.TrayUnread), 88 | warn: generateIcon(Icon.TrayPing) 89 | } 90 | }, 91 | minWinHeight: 412, 92 | minWinWidth: 312, 93 | backgroundColor: "#36393F" 94 | } as const); -------------------------------------------------------------------------------- /sources/code/common/modules/electron.ts: -------------------------------------------------------------------------------- 1 | /* electron.ts – electron-specific functions made to work independently of its process. */ 2 | import { app } from "electron/main"; 3 | import { existsSync } from "fs"; 4 | import { resolve } from "path"; 5 | import { promises as fso } from "node:original-fs"; 6 | import { createHash, getHashes } from "crypto"; 7 | import type { BinaryToTextEncoding } from "crypto"; 8 | 9 | function catchAndThrowErrors (error:unknown) { 10 | if(error instanceof Error) 11 | throw error; 12 | } 13 | 14 | /** The current application directory. Cross-process safe method. */ 15 | export function getAppPath() { 16 | if (process.type === "browser") 17 | return app.getAppPath(); 18 | else { 19 | // Calculate the project's directory based on the `package.json` position. 20 | let path = __dirname; 21 | while(!existsSync(resolve(path, "./package.json")) && !/^(?:\/|[A-Z]:\\)$/.test(path)) 22 | path = resolve(path, "../"); 23 | return path; 24 | } 25 | } 26 | 27 | /** Show a message box. Cross-process safe method. */ 28 | export function showMessageBox(options: Electron.MessageBoxOptions): void { 29 | if (process.type === "browser") { 30 | import("electron") 31 | .then(api => api.dialog.showMessageBox(options)) 32 | .catch(catchAndThrowErrors); 33 | } else { 34 | const title = options.title !== undefined ? options.title + "\n" : ""; 35 | alert(title + options.message); 36 | } 37 | } 38 | 39 | /** The current application locale. Cross-process safe method. */ 40 | export function getLocale() { 41 | if (process.type === "browser") { 42 | return process.platform !== "win32" || app.isReady() ? 43 | // Default method 44 | app.getLocale() : 45 | // Fallback on Windows. 46 | app.getLocaleCountryCode().toLowerCase(); 47 | } else 48 | return navigator.language; 49 | } 50 | 51 | /** 52 | * The current application name (fallbacks to `the application` on renderer 53 | * process). Cross-process safe method. 54 | * */ 55 | export function getName() { 56 | if (process.type === "browser") 57 | return app.getName(); 58 | else 59 | return "the application"; 60 | } 61 | 62 | /** 63 | * Get hash of current `app.asar` file in given algorithm. 64 | */ 65 | export async function getAppHash(algorithm = "sha512", encoding:BinaryToTextEncoding = "hex") { 66 | const file = getAppPath(); 67 | if(!getHashes().includes(algorithm)) 68 | throw new Error("Unsuported hashing algorithm: "+algorithm); 69 | if((await fso.stat(file)).isFile()) 70 | return fso.readFile(file) 71 | .then(buffer => createHash(algorithm).update(buffer).digest(encoding)); 72 | return; 73 | } -------------------------------------------------------------------------------- /sources/code/main/modules/bug.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * crash.ts – funtion/s to report a bug on GitHub. 3 | */ 4 | 5 | import { app } from "electron/main"; 6 | import { shell } from "electron/common"; 7 | import { appInfo } from "../../common/modules/client"; 8 | 9 | /** 10 | * Generates a link to new GitHub issue, based on `bug_report.md` 11 | * and current hardware / software configuration. This makes it 12 | * easy to aquire needed details (except screenshot, because of 13 | * the lack of the GitHub support to do that ~~via the CDN or~~ using 14 | * `base64` encoded images). 15 | * 16 | * @property reason – A bug description (app failure reason). 17 | * @todo Parse (commented) values to be compatible with the template. 18 | * @todo Check GitHub API to actually use their CDN if possible. 19 | */ 20 | export async function createGithubIssue(reason?: string): Promise { 21 | /** An URL to the new GitHub issue, based on YAML forms. */ 22 | const githubIssueUrl = new URL( 23 | appInfo.repository.name+"/issues/new", 24 | "https://github.com" 25 | ); 26 | /** A set of URL parameters appended to the {@link githubIssueUrl}. */ 27 | const githubIssueUrlParams = new URLSearchParams({ 28 | assignees: "SpacingBat3", 29 | labels: "bug", 30 | template: "bug.yml", 31 | //platform: "N/A", 32 | //architecture: "N/A", 33 | electron_version: process.versions.electron, 34 | app_version: app.getVersion(), 35 | additional: "**Notice:** This issue was automatically generated by " + app.getName() + "." 36 | }); 37 | 38 | githubIssueUrlParams 39 | .forEach((value,key) => githubIssueUrl.searchParams.append(key,value)); 40 | 41 | if(reason !== undefined) 42 | githubIssueUrl.searchParams.append("description", reason); 43 | 44 | /* Verify origin and open URL in default browser. */ 45 | 46 | if (githubIssueUrl.origin === "https://github.com") 47 | return shell.openExternal(githubIssueUrl.href); 48 | } -------------------------------------------------------------------------------- /sources/code/main/modules/optimize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Platform / hardware-specific Electron optimizations. 3 | */ 4 | 5 | /** Whenever the current process is ran on *nix. */ 6 | const isUnix = process.platform !== "win32" && process.platform !== "darwin"; 7 | 8 | const isWaylandNative = isUnix && process.argv.includes("--ozone-platform=wayland") || 9 | process.argv.includes("--ozone-hint=auto") || 10 | process.argv.includes("--ozone-hint=wayland"); 11 | 12 | const isWayland = isUnix && ( 13 | process.env["XDG_SESSION_TYPE"] === "wayland" || 14 | process.env["WAYLAND_DISPLAY"] !== undefined || 15 | isWaylandNative 16 | ); 17 | 18 | /** 19 | * An experimental function that might return the flags, which seem to improve 20 | * a graphics rendering performance. Some flags might enable the features not 21 | * fully tested to work with every GPU card. 22 | * 23 | * Unlike its name, Chromium developers seem to have the reason not to 24 | * enable this by the default – it is advised to test whenever this app performs 25 | * better or worse with these settings applied. 26 | */ 27 | export function getRecommendedGPUFlags() { 28 | const switches:([string]|[string,string])[] = []; 29 | // Use EGL on Wayland and ARM devices. 30 | if(isWayland || (isUnix && process.arch === "arm64")) 31 | switches.push(["use-gl", "egl"]); 32 | if(isUnix) { 33 | switches.push( 34 | // Enforce VA-API: 35 | ["enable-features", "VaapiVideoDecoder,VaapiVideoEncoder,VaapiIgnoreDriverChecks"], 36 | ["disable-features", "UseChromeOSDirectVideoDecoder"], 37 | // Bypass GPU blocklist: 38 | ["ignore-gpu-blocklist"], 39 | ["enable-zero-copy"] 40 | ); 41 | } 42 | return switches; 43 | } 44 | 45 | /** 46 | * A function to return information about recommended flags to improve 47 | * the app's integration within the OS. 48 | */ 49 | export function getRecommendedOSFlags() { 50 | const switches: ([string]|[string,string])[] = []; 51 | // Recommended switches when running on Wayland Native 52 | if(isWaylandNative) switches.push( 53 | ["enable-features","UseOzonePlatform,WebRTCPipeWireCapturer,WaylandWindowDecorations"] 54 | ); 55 | // Recommended switches for XWayland 56 | else if(isWayland) switches.push( 57 | ["enable-features","WebRTCPipeWireCapturer"] 58 | ); 59 | return switches; 60 | } -------------------------------------------------------------------------------- /sources/code/main/modules/parent.ts: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow, session } from "electron/main"; 2 | import L10N from "../../common/modules/l10n"; 3 | import { appInfo, getBuildInfo } from "../../common/modules/client"; 4 | import { resolve } from "path"; 5 | import { deepmerge } from "deepmerge-ts"; 6 | import { styles } from "./extensions"; 7 | import { commonCatches } from "./error"; 8 | import { fonts } from "../../common/global"; 9 | 10 | /** A list of popup windows (i.e. non-local ones). */ 11 | const popups = [ 12 | "invite" 13 | ]; 14 | 15 | /** 16 | * Initializes the new `BrowserWindow` that will be a child of `mainWindow`. 17 | * It will either create a such window or do nothing if it does already exists. 18 | * 19 | */ 20 | export function initWindow(name:keyof L10N["client"]["windows"], parent: Electron.BrowserWindow, properties?: Electron.BrowserWindowConstructorOptions) { 21 | const isPopup = popups.includes(name); 22 | if(!app.isReady()) throw new Error("Tried to initialize a new parent window when app is not ready!"); 23 | const wSession = isPopup ? session.defaultSession : session.fromPartition("temp:"+name); 24 | for (const window of parent.getChildWindows()) 25 | if(window.webContents.session === wSession) return; 26 | if(!parent.isVisible()) parent.show(); 27 | const win = new BrowserWindow(deepmerge<[Electron.BrowserWindowConstructorOptions,Electron.BrowserWindowConstructorOptions]>({ 28 | title: app.getName() + " – " + (new L10N()).client.windows[name], 29 | show: false, 30 | parent: parent, 31 | backgroundColor: appInfo.backgroundColor, 32 | webPreferences: { 33 | session: wSession, 34 | nodeIntegration: false, 35 | contextIsolation: true, 36 | sandbox: false, 37 | enableWebSQL: false, 38 | webgl: false, 39 | autoplayPolicy: "user-gesture-required", 40 | defaultFontFamily: fonts, 41 | ...( !isPopup ? { 42 | preload: resolve(app.getAppPath(), "app/code/renderer/preload/"+name+".js") 43 | } : {}), 44 | }, 45 | ...(process.platform !== "win32" ? {icon: appInfo.icons.app} : {}) 46 | }, properties??{})); 47 | if(win.webContents.session === parent.webContents.session && !isPopup) 48 | throw new Error("Child took session from parent!"); 49 | if(isPopup) { 50 | win.removeMenu(); 51 | // Style "popup" windows 52 | win.webContents.on("did-navigate", () => { 53 | styles.load(win.webContents) 54 | .catch(commonCatches.throw); 55 | }); 56 | } 57 | // Cleanup listeners 58 | win.once("closed", () => win.removeAllListeners()); 59 | win.setAutoHideMenuBar(true); 60 | win.setMenuBarVisibility(false); 61 | if(getBuildInfo().type === "release") win.removeMenu(); 62 | if(!isPopup) void win.loadFile(resolve(app.getAppPath(), "sources/assets/web/html/"+name+".html")); 63 | // Shows window when it is loading for too long. 64 | setTimeout( () => { if(!win.isDestroyed() && !win.isVisible()) win.show(); }, 10000 ); 65 | return win; 66 | } -------------------------------------------------------------------------------- /sources/code/main/modules/update.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * update – notifications about the updates 3 | */ 4 | 5 | import { app, Notification, net } from "electron/main"; 6 | import { shell } from "electron/common"; 7 | import { appInfo, getBuildInfo } from "../../common/modules/client"; 8 | import L10N from "../../common/modules/l10n"; 9 | import * as semver from "semver"; 10 | import kolor from "@spacingbat3/kolor"; 11 | import { commonCatches } from "./error"; 12 | import { appConfig } from "./config"; 13 | 14 | /** 15 | * Checks and notifies users about the updates. 16 | * 17 | * @param updateInterval Object that identifies a currently running interval. 18 | */ 19 | export async function checkVersion(updateInterval: NodeJS.Timeout | undefined): Promise { 20 | // When app is not ready, wait until it is ready. 21 | await app.whenReady(); 22 | // Initialize app translation. 23 | const strings = new L10N().client; 24 | // Do not execute when offline. 25 | if (!net.isOnline()) { 26 | console.debug(strings.dialog.ver.updateBadge + " Network is offline, skipping check..."); 27 | return; 28 | } 29 | // An alias to app's repository name. 30 | const repoName = appInfo.repository.name; 31 | let updateMsg: string, showGui = false; 32 | const githubApi = await ((await fetch("https://api.github.com/repos/" + repoName + "/releases/latest")).json() as Promise>); 33 | if(githubApi["tag_name"] === undefined || githubApi["html_url"] === undefined) return; 34 | switch(semver.compare(githubApi["tag_name"],app.getVersion())) { 35 | case 0: 36 | // Application version is the same as in tag 37 | updateMsg = strings.dialog.ver.recent; 38 | break; 39 | case 1: 40 | showGui = true; 41 | updateMsg = strings.dialog.ver.update + 42 | " (v" + app.getVersion() + " → v" + githubApi["tag_name"].replace(/^v(.+)$/,"$1") + ")"; 43 | break; 44 | case -1: 45 | // Application is newer than remote version. 46 | if(getBuildInfo().type === "devel") 47 | updateMsg = strings.dialog.ver.devel; 48 | else 49 | updateMsg = strings.dialog.ver.downgrade + 50 | " (v" + app.getVersion() + " → v" + githubApi["tag_name"].replace(/^v(.+)$/,"$1") + ")"; 51 | break; 52 | default: 53 | // If version can't be parsed by semver. 54 | throw new Error("Couldn't compare versions while doing an update"); 55 | } 56 | console.log(kolor.bold(kolor.blue(strings.dialog.ver.updateBadge)) + " " + updateMsg); 57 | const nextWeek = new Date(); 58 | nextWeek.setDate(nextWeek.getDate()+7); 59 | const ignored = ( 60 | appConfig.value.update.notification.version === githubApi["tag_name"] && 61 | new Date(appConfig.value.update.notification.till) < nextWeek 62 | ); 63 | if (showGui && (getBuildInfo().features.updateNotifications) && !ignored) { 64 | const notification = new Notification({ 65 | title: app.getName() + ": " + strings.dialog.ver.updateTitle, 66 | icon: appInfo.icons.app, 67 | body: updateMsg 68 | }); 69 | notification.on("click", () => { 70 | if(githubApi["html_url"] !== undefined) 71 | shell.openExternal(githubApi["html_url"]).catch(commonCatches.throw); 72 | }); 73 | notification.on("close", () => { 74 | if(githubApi["tag_name"] !== undefined) 75 | appConfig.value = { 76 | update: { 77 | notification: { 78 | version: githubApi["tag_name"], 79 | till: (JSON.parse(JSON.stringify(nextWeek)) as string) 80 | } 81 | } 82 | }; 83 | }); 84 | notification.show(); 85 | } 86 | if (updateInterval) 87 | clearInterval(updateInterval); 88 | } -------------------------------------------------------------------------------- /sources/code/main/windows/about.ts: -------------------------------------------------------------------------------- 1 | import { app, ipcMain as ipc, screen } from "electron/main"; 2 | import packageJson from "../../common/modules/package"; 3 | import { getBuildInfo } from "../../common/modules/client"; 4 | import { initWindow } from "../modules/parent"; 5 | 6 | // "About" Panel: 7 | 8 | export default function showAboutPanel(parent:Electron.BrowserWindow): Electron.BrowserWindow|undefined { 9 | const screenBounds = screen.getPrimaryDisplay().size; 10 | const [width, height] = [ 11 | (screenBounds.width < 600 ? screenBounds.width : 600), 12 | (screenBounds.height < 480 ? screenBounds.height : 480) 13 | ]; 14 | const aboutPanel = initWindow("about", parent, { 15 | width, 16 | height, 17 | resizable: false, 18 | fullscreenable: false, 19 | frame: false, 20 | modal: true 21 | }); 22 | const appDetails = { 23 | appName: app.getName(), 24 | appVersion: app.getVersion(), 25 | buildInfo: getBuildInfo(), 26 | appRepo: packageJson.data.homepage 27 | }; 28 | if(aboutPanel === undefined) return; 29 | ipc.once("about.close", (event) => { 30 | if(!aboutPanel.isDestroyed() && event.senderFrame?.url === aboutPanel.webContents.getURL()) 31 | aboutPanel.close(); 32 | }); 33 | ipc.once("about.readyToShow", (event) => { 34 | if(!aboutPanel.isDestroyed() && event.senderFrame?.url === aboutPanel.webContents.getURL()) 35 | aboutPanel.show(); 36 | }); 37 | ipc.handle("about.getDetails", (event) => { 38 | if(event.senderFrame?.url === aboutPanel.webContents.getURL()) 39 | return appDetails; 40 | return undefined; 41 | }); 42 | aboutPanel.once("close", () => { 43 | ipc.removeAllListeners("showAppLicense"); 44 | ipc.removeAllListeners("about.readyToShow"); 45 | ipc.removeAllListeners("about.close"); 46 | ipc.removeHandler("about.getDetails"); 47 | }); 48 | return aboutPanel; 49 | } -------------------------------------------------------------------------------- /sources/code/main/windows/docs.ts: -------------------------------------------------------------------------------- 1 | import { commonCatches } from "../modules/error"; 2 | 3 | async function handleEvents(docsWindow: Electron.BrowserWindow) { 4 | const [ 5 | existsSync, // from "fs" 6 | resolve, // from "path" 7 | e 8 | ] = [ 9 | import("fs").then(mod => mod.existsSync), 10 | import("path").then(mod => (...args:string[])=> mod.resolve(...args)), 11 | import("electron/main") 12 | ]; 13 | // Guess correct Readme.md file 14 | let readmeFile = "docs/Readme.md"; 15 | if((await existsSync)((await resolve)((await e).app.getAppPath(), "docs", (await e).app.getLocale(), "Readme.md"))) 16 | readmeFile = "docs/"+(await e).app.getLocale()+"/Readme.md"; 17 | (await e).ipcMain.removeHandler("documentation-load"); 18 | (await e).ipcMain.removeAllListeners("documentation-show"); 19 | (await e).ipcMain.handle("documentation-load", async (event) => { 20 | if(event.senderFrame?.url !== docsWindow.webContents.getURL()) return; 21 | (await e).ipcMain.once("documentation-show", (event) => { 22 | if(!docsWindow.isDestroyed() && event.senderFrame?.url === docsWindow.webContents.getURL()) { 23 | docsWindow.show(); 24 | } 25 | }); 26 | return (await resolve)((await e).app.getAppPath(), readmeFile); 27 | }); 28 | } 29 | 30 | export default async function loadDocsWindow(parent: Electron.BrowserWindow) { 31 | const [ 32 | { initWindow }, // from "../modules/parent" 33 | { appInfo }, // from "../modules/client" 34 | ] = await Promise.all([ 35 | import("../modules/parent.js"), 36 | import("../../common/modules/client.js") 37 | ]); 38 | const docsWindow = initWindow("docs", parent, { 39 | minWidth: appInfo.minWinWidth, 40 | minHeight: appInfo.minWinHeight, 41 | width: 720, 42 | height: 600 43 | }); 44 | if(docsWindow === undefined) return; 45 | handleEvents(docsWindow).catch(commonCatches.throw); 46 | } -------------------------------------------------------------------------------- /sources/code/main/windows/settings.ts: -------------------------------------------------------------------------------- 1 | import { ipcMain } from "electron/main"; 2 | import { appInfo } from "../../common/modules/client"; 3 | import L10N from "../../common/modules/l10n"; 4 | import { initWindow } from "../modules/parent"; 5 | import { deepmerge } from "deepmerge-ts"; 6 | import type { cspTP, AppConfig } from "../modules/config"; 7 | import type { PartialRecursive } from "../../common/global"; 8 | import { appConfig } from "../modules/config"; 9 | 10 | type generatedConfig = AppConfig["settings"] & L10N["settings"] & { 11 | advanced: { 12 | cspThirdParty: { 13 | labels: Record, string>; 14 | info: Record, string>; 15 | }; 16 | }; 17 | }; 18 | 19 | function generateConfig () { 20 | const config = deepmerge(appConfig.value.settings, new L10N().settings); 21 | const finalConfig: PartialRecursive = config as object; 22 | const websitesThirdParty = Object.freeze(({ 23 | algolia: "Algolia", 24 | spotify: "Spotify", 25 | hcaptcha: "hCaptcha", 26 | paypal: "PayPal", 27 | audius: "Audius", 28 | gif: config.advanced.cspThirdParty.labels.gif, 29 | reddit: "Reddit", 30 | soundcloud: "SoundCloud", 31 | streamable: "Streamable", 32 | twitch: "Twitch", 33 | twitter: "Twitter", 34 | vimeo: "Vimeo", 35 | youtube: "YouTube", 36 | googleStorageApi: "Google Storage API" 37 | } as const) satisfies cspTP); 38 | // Append more third-party sites labels. 39 | Object.entries(websitesThirdParty).map(stringGroup => { 40 | if(finalConfig.advanced?.cspThirdParty?.labels && finalConfig.advanced.cspThirdParty.labels[stringGroup[0] as keyof cspTP] === undefined) 41 | finalConfig.advanced.cspThirdParty.labels[stringGroup[0] as keyof cspTP] = stringGroup[1]; 42 | if(finalConfig.advanced?.cspThirdParty) 43 | if(finalConfig.advanced.cspThirdParty.info !== undefined) 44 | finalConfig.advanced.cspThirdParty.info["gif"] = "Imgur, Gfcat, Tenor"; 45 | else 46 | finalConfig.advanced.cspThirdParty.info = { 47 | gif: "Imgur, Gfcat, Tenor" 48 | }; 49 | }); 50 | // Append name from CSP. 51 | if(finalConfig.advanced?.cspThirdParty?.name !== undefined) 52 | finalConfig.advanced.cspThirdParty.name = config.advanced.csp.name + " – " + config.advanced.cspThirdParty.name; 53 | return finalConfig as generatedConfig; 54 | } 55 | 56 | type htmlConfigElement = readonly [T,generatedConfig[T]]; 57 | 58 | export type htmlConfig = readonly ( 59 | Exclude extends infer T extends keyof generatedConfig ? htmlConfigElement : never 60 | )[]; 61 | 62 | export default function loadSettingsWindow(parent:Electron.BrowserWindow):Electron.BrowserWindow|undefined { 63 | const config = generateConfig(); 64 | const htmlConfig = Object.freeze([ 65 | Object.freeze(["general", config.general] as const), 66 | Object.freeze(["privacy", config.privacy] as const), 67 | Object.freeze(["advanced", config.advanced] as const) 68 | ] as const) satisfies htmlConfig; 69 | if(!parent.isVisible()) parent.show(); 70 | const settingsWindow = initWindow("settings", parent, { 71 | minWidth: appInfo.minWinWidth, 72 | minHeight: appInfo.minWinHeight, 73 | }); 74 | if(settingsWindow === undefined) return; 75 | ipcMain.handle("settings-generate-html", (event) => { 76 | if(event.senderFrame && new URL(event.senderFrame.url).protocol !== "file:") 77 | return; 78 | if(!settingsWindow.isDestroyed()) settingsWindow.show(); 79 | return htmlConfig; 80 | }); 81 | settingsWindow.once("closed", () => { 82 | ipcMain.removeHandler("settings-generate-html"); 83 | }); 84 | return settingsWindow; 85 | } -------------------------------------------------------------------------------- /sources/code/renderer/modules/api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * A place to move useful WebCord's function that could be exposed to 3 | * third-party addons in the future as part of planned "API". 4 | */ 5 | 6 | function randomInt(min = 0, max = 255) { 7 | if(min < 0 || max > 255) 8 | throw new RangeError("Parameters 'min' and 'max' out of range of type 'u8'."); 9 | let random: number|undefined; 10 | let maxTries = 30; 11 | while (maxTries > 0) { 12 | random = crypto.getRandomValues(new Uint8Array([0]))[0]; 13 | if(random !== undefined && random >= min && random <= max) break; 14 | maxTries--; 15 | } 16 | if(random === undefined) 17 | throw new Error("Couldn't generate a valid pseudo-random number!"); 18 | return random; 19 | } 20 | 21 | /** 22 | * Generates a random key of `window` that can safely be used as global variable 23 | * name in DOM scripts (both in means of being hard to detect and by not using 24 | * the already existing key name). 25 | */ 26 | export function generateSafeKey() { 27 | let key; 28 | do { 29 | key = ""; 30 | for(let i=0; i<=randomInt(4,32); i++) { 31 | const cc = randomInt(0,51); 32 | key += String.fromCharCode(cc+(cc>25?71:65)); 33 | } 34 | } while(key === "" || key in window); 35 | return key; 36 | } 37 | 38 | /** 39 | * Gets list of the elements with `tagName` tag name that has any class assigned 40 | * which its name includes the `searchString`. This tries to replicate the 41 | * similar behaviour as the one achieved by the `.getElementsByClassName` 42 | * method, except it can allow for part of the class names as an input. 43 | * 44 | * This can be useful when trying to tweak the elements whose class names 45 | * includes some part being randomly generated for each build/version. 46 | * 47 | * **Currently unused in the code itself, reserved for WebCord's future API for 48 | * the extensions.** 49 | *//* 50 | export function findClass(searchString: S, tagName: keyof HTMLElementTagNameMap) { 51 | const searchResult = new Set<`${string}${S}${string}`>(); 52 | for (const container of document.getElementsByTagName(tagName)) 53 | for (const classString of container.classList) 54 | if(classString.includes(searchString)) 55 | searchResult.add(classString as `${string}${S}${string}`); 56 | return [...searchResult]; 57 | }/**/ 58 | 59 | /** 60 | * A function that allows the access to the Discord API without the need of 61 | * sharing the user token to the untrusted scripts. Currently at 62 | * **Work-in-Progress** state. 63 | * 64 | * Currently unused in the code itself, reserved for WebCord's future API for 65 | * the extensions.** 66 | * 67 | * @param method 68 | * @param apiVersion 69 | * @param endpoint 70 | *//* 71 | export function sendRequest(method:"POST"|"GET", apiVersion: 6|7|8|9|10, endpoint:string, body?: XMLHttpRequestBodyInit) { 72 | const request = new XMLHttpRequest(); 73 | request.open(method,new URL(document.URL).origin+"/api/v"+apiVersion.toString()+endpoint); 74 | window.addEventListener("load", () => { 75 | const token = window.localStorage.getItem("token"); 76 | if(token !== null) { 77 | request.setRequestHeader("Authorization", token); 78 | request.send(body); 79 | return; 80 | } 81 | request.abort(); 82 | return; 83 | }); 84 | }/**/ 85 | 86 | /** 87 | * Allows to navigate to the given path without reloading the entire Discord 88 | * page. In WebCord, this is used to handle `DEEP_LINK` requests. 89 | */ 90 | export function navigate(path:string) { 91 | // Push new state to history. 92 | history.pushState({}, "", path); 93 | // "Reload" history so Discord/Chromium can properly handle it. 94 | window.addEventListener("popstate", () => history.forward(), {once: true}); 95 | history.back(); 96 | } -------------------------------------------------------------------------------- /sources/code/renderer/preload/main.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer as ipc } from "electron/renderer"; 2 | import { clipboard } from "electron/common"; 3 | import { generateSafeKey, navigate } from "../modules/api"; 4 | import { wLog } from "../../common/global"; 5 | import { appInfo } from "../../common/modules/client"; 6 | import L10N from "../../common/modules/l10n"; 7 | 8 | if (window.location.protocol === "file:") { 9 | window.addEventListener("load", () => { 10 | const element = document.getElementById("logo"); 11 | if(element && element.tagName === "IMG") 12 | (element as HTMLImageElement).src = appInfo.icons.app.toDataURL(); 13 | }); 14 | contextBridge.exposeInMainWorld( 15 | "webcord", 16 | { 17 | l10n: (new L10N()).web 18 | } 19 | ); 20 | } else { 21 | /** 22 | * WebCord API key used as the object name of the exposed content 23 | * by the Context Bridge. 24 | */ 25 | const contextBridgeApiKey = generateSafeKey(); 26 | 27 | /* 28 | * Expose API key back to the main process. 29 | */ 30 | ipc.send("api-exposed", contextBridgeApiKey); 31 | 32 | /* 33 | * Hide orange popup about downloading the application. 34 | */ 35 | window.addEventListener("load", () => window.localStorage.setItem("hideNag", "true")); 36 | 37 | /* 38 | * Workaround for clipboard content. 39 | */ 40 | { 41 | let lock = true; 42 | document.addEventListener("paste", (event) => { 43 | const contentTypes = clipboard.availableFormats() as []|[string, string]; 44 | if(contentTypes.length === 2 && contentTypes[0].startsWith("image/") && 45 | contentTypes[1] === "text/html" && lock) { 46 | console.debug("[WebCord] Applying clipboard workaround to the image…"); 47 | lock = false; 48 | // Electron will somehow sort the clipboard to parse it correctly. 49 | clipboard.write({ 50 | image: clipboard.readImage(), 51 | html: clipboard.readHTML() 52 | }); 53 | // Retry event, cancel other events. 54 | event.stopImmediatePropagation(); 55 | ipc.send("paste-workaround", contextBridgeApiKey); 56 | return; 57 | } 58 | lock = true; 59 | return; 60 | }, true); 61 | } 62 | 63 | /* 64 | * Handle WebSocket Server IPC communication 65 | */ 66 | ipc.on("navigate", (_event, path:string) => { 67 | navigate(path); 68 | }); 69 | } 70 | 71 | wLog("Everything has been preloaded successfully!"); -------------------------------------------------------------------------------- /sources/code/utility/ws.ts: -------------------------------------------------------------------------------- 1 | import { WebSocket, type HookSignatures } from "@spacingbat3/disconnection"; 2 | import { knownInstancesList, type WSHookAdd, type WSHookTrigger, type WSHookReturn } from "../common/global"; 3 | import { EventEmitter } from "events"; 4 | 5 | const eventLoop = new EventEmitter(); 6 | 7 | const server = new WebSocket([ 8 | ...knownInstancesList.map(instance => instance[1].origin) 9 | ]); 10 | 11 | const nonceSet = new Set(); 12 | 13 | process.parentPort.on("message", ({data}: {data:unknown}) => void (async() => { 14 | const port = (await server.details)?.port; 15 | if(typeof data === "string") { 16 | server.log(data,port); 17 | return; 18 | } 19 | const message: WSHookAdd|WSHookReturn = data as WSHookAdd|WSHookReturn; 20 | const { hook } = message; 21 | switch(message.evt) { 22 | case "hook-set": 23 | server.addHook(hook, (...data) => new Promise((resolve,reject) => { 24 | const nonceSymbol = Symbol(); 25 | nonceSet.add(nonceSymbol); 26 | const nonce = [...nonceSet].indexOf(nonceSymbol); 27 | process.parentPort.postMessage({ 28 | evt: "hook-trigger", 29 | hook, 30 | data, 31 | nonce, 32 | port 33 | } satisfies WSHookTrigger); 34 | eventLoop.once(nonceSymbol, (value?:number|Error)=>{ 35 | if(value instanceof Error) 36 | reject(value); 37 | else 38 | resolve(value); 39 | }); 40 | })); 41 | break; 42 | case "hook-return": { 43 | const event = [...nonceSet][message.nonce]; 44 | if(event) 45 | eventLoop.emit(event,message.data); 46 | break; 47 | } 48 | } 49 | })()); -------------------------------------------------------------------------------- /sources/translations/ar/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": { 3 | "copy": "انسخ", 4 | "cut": "قص", 5 | "dictionaryAdd": "أضف للقاموس المحلي", 6 | "copyURL": "انسخ عنوان الرابط", 7 | "copyURLText": "انسخ نص الرابط", 8 | "inspectElement": "افحص", 9 | "paste": "الصق", 10 | "copyImage": "انسخ الصورة", 11 | "copyImageURL": "انسخ رابط الصورة" 12 | }, 13 | "menubar": { 14 | "view": { 15 | "reload": "أعد التحميل", 16 | "groupName": "العرض", 17 | "forceReload": "افرض إعادة التحميل", 18 | "zoomOut": "صغّر العرض", 19 | "zoomIn": "كبّر العرض", 20 | "resetZoom": "الحجم الفعلي", 21 | "devTools": "بدل أدوات المطور", 22 | "fullScreen": "بدل وضع ملء الشاشة" 23 | }, 24 | "enabled": "مُفعَّل", 25 | "file": { 26 | "groupName": "ملف", 27 | "quit": "أنهِ", 28 | "relaunch": "أعد التشغيل", 29 | "addon": { 30 | "groupName": "الإضافات", 31 | "loadNode": "حمل إضافة Node.js", 32 | "loadChrome": "حمل إضافة كروم" 33 | } 34 | }, 35 | "edit": { 36 | "groupName": "تحرير", 37 | "undo": "تراجع", 38 | "redo": "أعِد", 39 | "selectAll": "حدِّد الكلَّ" 40 | }, 41 | "window": { 42 | "groupName": "النافذة", 43 | "mobileMode": "أخفِ الشريط الجانبي" 44 | } 45 | }, 46 | "windows": { 47 | "about": "حول", 48 | "docs": "التوثيق", 49 | "invite": "ادعُ", 50 | "settings": "الإعدادات" 51 | }, 52 | "dialog": { 53 | "common": { 54 | "error": "خطأ", 55 | "warning": "تحذير", 56 | "continue": "&استمر", 57 | "yes": "&نعم", 58 | "no": "&لا", 59 | "source": "المصدر" 60 | }, 61 | "ver": { 62 | "updateTitle": "يتوفر تحديث!", 63 | "update": "يتوفر إصدار جديد!", 64 | "devel": "أنت تستخدم الإصدار التجريبي.", 65 | "downgrade": "إصدارك أحدث من الإصدار المستقر!", 66 | "updateBadge": "[تحديث]", 67 | "recent": "التطبيق محدث!" 68 | }, 69 | "permission": { 70 | "request": { 71 | "denied": "%s: رُفض طلب منح الوصول لـ %s." 72 | }, 73 | "check": { 74 | "denied": "%s: رُفض طلب التحقق من %s." 75 | }, 76 | "question": { 77 | "title": "مُعالج طلب الأذونات", 78 | "message": "هل تريد منح دسكورد حق الوصول لـ %s؟", 79 | "video": "الكامرة", 80 | "audio": "الميكروفون", 81 | "notifications": "الإشعارات" 82 | } 83 | }, 84 | "mod": { 85 | "crx": "إضافة كروم/كروميوم", 86 | "nodeExt": "إضافة Node.js لوِبكورد" 87 | }, 88 | "externalApp": { 89 | "title": "فتح الرابط في تطبيق خارجي", 90 | "message": "يحاول موقع إلكتروني إعادة توجيه الرابط لتطبيق خارجي. هل تريد الاستمرار؟" 91 | }, 92 | "screenShare": { 93 | "close": "أغلق", 94 | "sound": { 95 | "unavailable": "لا تتوفر ميزة مشاركة الصوت في هذه المنصة.", 96 | "system": "شارك مع صوت الجهاز." 97 | }, 98 | "source": { 99 | "entire": "شاشة الحاسوب", 100 | "screen": "الشاشة" 101 | } 102 | } 103 | }, 104 | "help": { 105 | "groupName": "المساعدة", 106 | "repo": "المستودع", 107 | "bugs": "بلّغ عن خطأ" 108 | }, 109 | "log": { 110 | "singleInstance": "التبديل إلى النافذة الحالية...", 111 | "listenPort": "يُستمع في المنفذ %s." 112 | }, 113 | "tray": { 114 | "toggle": "بدّل", 115 | "quit": "أنهِ", 116 | "mention": { 117 | "one": "ذكرى", 118 | "other": "ذكريات" 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sources/translations/ar/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "description": "تأكد من أنك متصل بالإنترنت.", 4 | "title": "تعذر الاتصال بخوادم دِسكُورد." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "credits": "الإشادات", 9 | "licenses": "الرُّخص", 10 | "about": "حول" 11 | }, 12 | "about": { 13 | "appDescription": "عميل لدسكورد ووِبكورد بمزايا تركز على الخصوصية.", 14 | "appRepo": "مستودع GitHub", 15 | "featuresTitle": "خيارات البناء", 16 | "featuresDefault": "افتراضي", 17 | "checksumTitle": "المجموع الاختباري" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "المؤلف والمُصين والمطور الرئيس.", 22 | "contributors": { 23 | "default": "مساهم في البرمجة." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "رخصة التطبيق", 29 | "appLicenseBody": "%s تطبيق حر، تستطيع استخدامه وتعديله وإعادة نشره حسب شروط ترخيص MIT الذي يجب أن يُنشر مع هذا البرنامج.", 30 | "showLicense": "أظهر الترخيص", 31 | "thirdPartyLicensesTitle": "تراخيص الجهات الخارجية", 32 | "thirdPartyLicensesBody": "يعتمد %s على برامج الجهات الخارجية التالية:", 33 | "licensedUnder": "بموجب ترخيص %s", 34 | "packageAuthors": "مطوروا %s" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/ca/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": { 3 | "paste": "Enganxa", 4 | "copyURLText": "Copia el text de l'enllaç", 5 | "inspectElement": "Inspeccionar", 6 | "copy": "Còpia", 7 | "cut": "Talla", 8 | "dictionaryAdd": "Afegeix al diccionari local", 9 | "copyURL": "Copia l'adreça de l'enllaç", 10 | "copyImageURL": "Copia l'enllaç de la imatge", 11 | "copyImage": "Copia la imatge" 12 | }, 13 | "menubar": { 14 | "window": { 15 | "mobileMode": "Amaga la barra lateral", 16 | "groupName": "Finestra" 17 | }, 18 | "view": { 19 | "zoomOut": "Redueix", 20 | "groupName": "Vista", 21 | "resetZoom": "Mida actual", 22 | "reload": "Recarregar", 23 | "fullScreen": "Commuta la pantalla completa", 24 | "forceReload": "Força la recàrrega", 25 | "devTools": "Commuta les eines per a desenvolupadors", 26 | "zoomIn": "Amplia" 27 | }, 28 | "file": { 29 | "quit": "Surt", 30 | "relaunch": "Rellança", 31 | "addon": { 32 | "groupName": "Extensions", 33 | "loadChrome": "Carrega l'extensió de Chrome", 34 | "loadNode": "Carrega l'extensió del node" 35 | }, 36 | "groupName": "Fitxer" 37 | }, 38 | "edit": { 39 | "groupName": "Edita", 40 | "undo": "Desfer", 41 | "redo": "Refer", 42 | "selectAll": "Selecciona tot" 43 | }, 44 | "enabled": "Habilitat" 45 | }, 46 | "dialog": { 47 | "common": { 48 | "yes": "&Sí", 49 | "no": "&No", 50 | "source": "Font", 51 | "warning": "Advertiment", 52 | "error": "Error", 53 | "continue": "&Continua" 54 | }, 55 | "ver": { 56 | "updateBadge": "[ACTUALITZACIÓ]", 57 | "updateTitle": "Actualització disponible!", 58 | "update": "Hi ha disponible una nova versió de l'aplicació!", 59 | "recent": "L'aplicació està actualitzada!", 60 | "devel": "Esteu utilitzant la versió que no és de producció.", 61 | "downgrade": "La vostra construcció de producció és més nova que la estable!" 62 | }, 63 | "permission": { 64 | "request": { 65 | "denied": "%s: S'ha denegat la sol·licitud de permís a %s." 66 | }, 67 | "check": { 68 | "denied": "%s: Comprovació de permisos a %s denegada." 69 | }, 70 | "question": { 71 | "video": "càmera", 72 | "audio": "micròfon", 73 | "notifications": "notificacions", 74 | "message": "Voleu donar a Discord access a %s?", 75 | "title": "Manipulador de sol·licitud de permís" 76 | } 77 | }, 78 | "mod": { 79 | "nodeExt": "Complement de WebCord Node.js", 80 | "crx": "Extensió de Chrome/Chromium" 81 | }, 82 | "externalApp": { 83 | "title": "S'està obrint l'enllaç a l'aplicació externa", 84 | "message": "Un lloc web intenta redirigir l'enllaç amb el diferent origen a l'aplicació externa. Continuar igualment?" 85 | }, 86 | "screenShare": { 87 | "close": "Tanca", 88 | "sound": { 89 | "unavailable": "La compartició d'àudio no està disponible en aquesta plataforma.", 90 | "system": "Comparteix l'àudio amb el dispositiu." 91 | }, 92 | "source": { 93 | "entire": "Pantalla de l'ordinador", 94 | "screen": "Pantalla" 95 | } 96 | } 97 | }, 98 | "tray": { 99 | "toggle": "Commuta", 100 | "quit": "Sortir", 101 | "mention": { 102 | "one": "menció", 103 | "other": "mencions" 104 | } 105 | }, 106 | "help": { 107 | "groupName": "Ajuda", 108 | "repo": "Repositori", 109 | "bugs": "Informar d'un error" 110 | }, 111 | "log": { 112 | "singleInstance": "S'està canviant a la finestra existent...", 113 | "listenPort": "Escoltant al port %s." 114 | }, 115 | "windows": { 116 | "docs": "Documentació", 117 | "invite": "Convidar", 118 | "settings": "Configuració", 119 | "about": "Quant a" 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sources/translations/ca/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "menuBar": { 4 | "labels": { 5 | "hide": "Amaga la barra de menús automàticament (la tecla ALT canvia la visibilitat)." 6 | }, 7 | "name": "Barra de menús", 8 | "description": "Canvia la configuració de visibilitat de la barra de menús nativa." 9 | }, 10 | "tray": { 11 | "name": "Safata", 12 | "description": "Canvia la visibilitat de la icona a la safata del sistema.", 13 | "labels": { 14 | "disable": "Desactiva l'ocultació de la finestra a la funcionalitat de la safata del sistema." 15 | } 16 | }, 17 | "taskbar": { 18 | "description": "Controla el comportament de l'entrada a la barra de tasques de WebCord.", 19 | "name": "Barra de tasques" 20 | }, 21 | "name": "General" 22 | }, 23 | "privacy": { 24 | "name": "Privacitat", 25 | "blockApi": { 26 | "name": "Bloqueig de l'API de Discord", 27 | "labels": { 28 | "typingIndicator": "Bloqueja l'indicació d'escriptura (/typing).", 29 | "science": "Bloqueja els punts finals de telemetria coneguts (/science i /tracing).", 30 | "fingerprinting": "Bloqueja els mètodes coneguts d'empremta digital (api.js i cdn-cgi)." 31 | }, 32 | "description": "Bloqueja les sol·licituds de l'API de Discord per endurir la privadesa." 33 | }, 34 | "permissions": { 35 | "name": "Permisos", 36 | "labels": { 37 | "video": "Càmera", 38 | "fullscreen": "Pantalla completa", 39 | "display-capture": "Captura d'escriptori", 40 | "background-sync": "Sincronització de fons", 41 | "notifications": "Notificacions", 42 | "audio": "Micròfon" 43 | }, 44 | "description": "Permet o denega les comprovacions de permisos i la sol·licitud des del lloc web de Discord. Tingueu en compte que WebCord bloqueja automàticament altres permisos que no s'inclouen aquí." 45 | } 46 | }, 47 | "advanced": { 48 | "name": "Avançat", 49 | "csp": { 50 | "name": "Política de seguretat de contingut" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sources/translations/ca/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "No es pot connectar al servei de Discord.", 4 | "description": "Assegureu-vos que esteu connectat a Internet." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "Quant a", 9 | "credits": "Crèdits", 10 | "licenses": "Llicències" 11 | }, 12 | "about": { 13 | "appDescription": "Client web Discord i Fosscord amb funcions enfocades a la privadesa.", 14 | "appRepo": "Repositori GitHub", 15 | "featuresDefault": "Per defecte", 16 | "checksumTitle": "Suma de control", 17 | "featuresTitle": "Banderes de construcció" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Autor, mantenidor i desenvolupador principal.", 22 | "contributors": { 23 | "default": "Col·laborador de codi." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "Llicència d'aplicació", 29 | "appLicenseBody": "%s és un programari lliure: podeu utilitzar-lo, modificar-lo i redistribuir-lo sota els termes de la llicència MIT, que s'hauria de distribuir amb aquest programari.", 30 | "showLicense": "Mostra la llicència", 31 | "thirdPartyLicensesTitle": "Llicències de tercers", 32 | "thirdPartyLicensesBody": "%s depèn del programari de tercers següent:", 33 | "licensedUnder": "sota la llicència %s", 34 | "packageAuthors": "%s autors" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/de/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": { 3 | "cut": "Ausschneiden", 4 | "copyURL": "Link Adresse kopieren", 5 | "copyURLText": "Link Text kopieren", 6 | "inspectElement": "Untersuchen", 7 | "copy": "Kopieren", 8 | "paste": "Einfügen", 9 | "dictionaryAdd": "Dem lokalen Wörterbuch hinzufügen", 10 | "copyImage": "Bild kopieren", 11 | "copyImageURL": "Link zum Bild kopieren" 12 | }, 13 | "menubar": { 14 | "enabled": "Aktiviert", 15 | "file": { 16 | "groupName": "Datei", 17 | "quit": "Beenden", 18 | "addon": { 19 | "groupName": "Erweiterungen", 20 | "loadChrome": "Chrome-Erweiterung laden", 21 | "loadNode": "Lade Node.js erweiterung" 22 | }, 23 | "relaunch": "Neu starten" 24 | }, 25 | "edit": { 26 | "groupName": "Bearbeiten", 27 | "undo": "Rückgängig machen", 28 | "redo": "Wiederherstellen", 29 | "selectAll": "Alles auswählen" 30 | }, 31 | "view": { 32 | "reload": "Neu laden", 33 | "forceReload": "Neuladen erzwingen", 34 | "resetZoom": "Tatsächliche Größe", 35 | "zoomIn": "Vergrößern", 36 | "fullScreen": "Vollbild ein-/ausblenden", 37 | "groupName": "Ansicht", 38 | "devTools": "Entwickler-Tools umschalten", 39 | "zoomOut": "Verkleinern" 40 | }, 41 | "window": { 42 | "groupName": "Fenster", 43 | "mobileMode": "Seitenleiste ausblenden" 44 | } 45 | }, 46 | "windows": { 47 | "settings": "Einstellungen", 48 | "about": "Über", 49 | "invite": "Einladen", 50 | "docs": "Dokumentation" 51 | }, 52 | "tray": { 53 | "toggle": "Umschalten", 54 | "quit": "Beenden", 55 | "mention": { 56 | "one": "erwähnen", 57 | "other": "erwähnt", 58 | "two": "Erwähnungen" 59 | } 60 | }, 61 | "dialog": { 62 | "common": { 63 | "error": "Fehler", 64 | "warning": "Warnung", 65 | "continue": "&Weiter", 66 | "yes": "&Ja", 67 | "no": "&Nein", 68 | "source": "Quelle" 69 | }, 70 | "ver": { 71 | "updateBadge": "[UPDATE]", 72 | "recent": "Anwendung ist aktuell!", 73 | "devel": "Du verwendest den Nicht-Produktions-Build.", 74 | "updateTitle": "Update verfügbar!", 75 | "update": "Eine neue Version der Anwendung ist verfügbar!", 76 | "downgrade": "Dein Produktions-Build ist neuer als Stabil!" 77 | }, 78 | "mod": { 79 | "crx": "Chrome/Chromium-Erweiterung", 80 | "nodeExt": "WebCord Node.js Erweiterung" 81 | }, 82 | "externalApp": { 83 | "title": "Link in externer Anwendung öffnen", 84 | "message": "Eine Website versucht, den Link mit einem anderen Ursprung auf eine externe Anwendung umzuleiten. Trotzdem weitermachen?" 85 | }, 86 | "permission": { 87 | "request": { 88 | "denied": "%s: Berechtigungsanforderung an %s abgelehnt." 89 | }, 90 | "check": { 91 | "denied": "%s: Berechtigungsprüfung auf %s abgelehnt." 92 | }, 93 | "question": { 94 | "message": "Möchtest du Discord Zugriff auf \"%s\" gewähren?", 95 | "video": "Kamera", 96 | "audio": "Mikrofon", 97 | "notifications": "Benachrichtigungen", 98 | "title": "Berechtigungsanfrage-Handler" 99 | } 100 | }, 101 | "screenShare": { 102 | "close": "Schließen", 103 | "sound": { 104 | "unavailable": "Das Teilen von Audio ist auf dieser Plattform nicht verfügbar.", 105 | "system": "Mit Geräteaudio teilen." 106 | }, 107 | "source": { 108 | "entire": "Computerbildschirm", 109 | "screen": "Bildschirm" 110 | } 111 | } 112 | }, 113 | "help": { 114 | "groupName": "Hilfe", 115 | "repo": "Repository", 116 | "bugs": "Einen Fehler melden" 117 | }, 118 | "log": { 119 | "singleInstance": "Wechsel zum vorhandenen Fenster...", 120 | "listenPort": "Lauschen an Port %s." 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /sources/translations/de/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "aboutWindow": { 3 | "nav": { 4 | "about": "Über", 5 | "credits": "Mitwirkende", 6 | "licenses": "Lizenzen" 7 | }, 8 | "about": { 9 | "appDescription": "Discord- und Fosscord-Webclient mit datenschutzfreundlichen Funktionen.", 10 | "appRepo": "GitHub-Repository", 11 | "featuresDefault": "Standard", 12 | "checksumTitle": "Prüfsumme", 13 | "featuresTitle": "Buildparameter" 14 | }, 15 | "credits": { 16 | "people": { 17 | "author": "Autor, Maintainer und Hauptentwickler.", 18 | "contributors": { 19 | "default": "Code-Mitwirkender." 20 | } 21 | } 22 | }, 23 | "licenses": { 24 | "appLicenseBody": "%s ist eine freie Software: DU kannst sie unter den Bedingungen der MIT-Lizenz, die mit dieser Software kommt, verwenden, verändern und weitergeben.", 25 | "showLicense": "Lizenz anzeigen", 26 | "thirdPartyLicensesTitle": "Lizenzen von Drittanbietern", 27 | "thirdPartyLicensesBody": "%s hängt von der folgenden Software von Drittanbietern ab:", 28 | "licensedUnder": "unter %s-Lizenz", 29 | "packageAuthors": "%s Autoren", 30 | "appLicenseTitle": "Anwendungslizenz" 31 | } 32 | }, 33 | "offline": { 34 | "title": "Es kann keine Verbindung zu Discord hergestellt werden.", 35 | "description": "Bitte stelle sicher, dass du mit dem Internet verbunden sind." 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/el/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": { 3 | "inspectElement": "Επιθεώρηση", 4 | "copy": "Αντιγραφή", 5 | "paste": "Επικόλληση", 6 | "cut": "Αποκοπή", 7 | "copyURLText": "Αντιγραφή κειμένου συνδέσμου", 8 | "copyURL": "Αντιγραφή διεύθυνσης συνδέσμου", 9 | "dictionaryAdd": "Προσθήκη στο τοπικό λεξικό" 10 | }, 11 | "menubar": { 12 | "edit": { 13 | "groupName": "Επεξεργασία", 14 | "undo": "Αναίρεση", 15 | "redo": "Επαναφορά" 16 | }, 17 | "view": { 18 | "groupName": "Προβολή", 19 | "reload": "Επαναφόρτωση", 20 | "forceReload": "Εξαναγκασμένη επαναφόρτωση", 21 | "devTools": "Ενεργοποίση Εργαλείων προγραμματιστή", 22 | "resetZoom": "Πραγματικό μέγεθος", 23 | "zoomIn": "Μεγέθυνση", 24 | "zoomOut": "Σμίκρυνση", 25 | "fullScreen": "Λειτουργεία πλήρους οθόνης" 26 | }, 27 | "window": { 28 | "groupName": "Παράθυρο", 29 | "mobileMode": "Απόκρυψη πλευρικής μπάρας" 30 | }, 31 | "file": { 32 | "groupName": "Αρχείο", 33 | "relaunch": "Επανεκκίνηση", 34 | "addon": { 35 | "groupName": "Επεκτάσεις", 36 | "loadChrome": "Φόρτωση επέκτασης Chrome", 37 | "loadNode": "Φόρτωση επέκτασης κόμβου" 38 | }, 39 | "quit": "Κλείσιμο προγράμματος" 40 | }, 41 | "enabled": "Ενεργοποιημένη" 42 | }, 43 | "windows": { 44 | "settings": "Ρυθμίσεις", 45 | "about": "Περί", 46 | "docs": "Εγχειρίδιο", 47 | "invite": "Πρόσκληση" 48 | }, 49 | "dialog": { 50 | "common": { 51 | "error": "Σφάλμα", 52 | "warning": "Προειδοποίηση", 53 | "continue": "&Συνεχίστε", 54 | "yes": "&Ναι", 55 | "no": "&Όχι", 56 | "source": "Πηγή" 57 | }, 58 | "ver": { 59 | "updateBadge": "[ΕΝΗΜΕΡΩΣΗ]", 60 | "updateTitle": "Διαθέσιμη ενημέρωση!", 61 | "update": "Η νέα έκδοση της εφαρμογής είναι διαθέσιμη!", 62 | "devel": "Χρησιμοποιείτε την υπό ανάπτυξη εκδοσή.", 63 | "recent": "Η εφαρμογή είναι ενημερωμένη!", 64 | "downgrade": "Η εκδοσή σας είναι νεότερη της σταθερής!" 65 | }, 66 | "permission": { 67 | "request": { 68 | "denied": "%s: Απορρίφθηκε η αίτηση άδειας για %s." 69 | }, 70 | "check": { 71 | "denied": "%s: Απορρίφθηκε ο έλεγχος άδειας για %s." 72 | } 73 | }, 74 | "mod": { 75 | "nodeExt": "Πρόσθετο WebCord Node.js", 76 | "crx": "Επέκταση Chrome/Chromium" 77 | }, 78 | "externalApp": { 79 | "title": "Άνοιγμα συνδέσμου σε εξωτερική εφαρμογή", 80 | "message": "Ένας ιστότοπος προσπαθεί να ανακατευθύνει σύνδεσμο διαφορετικής προέλευσης στην εξωτερική εφαρμογή. Συνεχίστε ούτως ή άλλως;" 81 | }, 82 | "screenShare": { 83 | "close": "Κλείσιμο", 84 | "sound": { 85 | "unavailable": "Η κοινή χρήση ήχου δεν είναι διαθέσιμη σε αυτή την πλατφόρμα.", 86 | "system": "Κοινή χρήση με τον ήχο της συσκευής." 87 | }, 88 | "source": { 89 | "entire": "Οθόνη υπολογιστή", 90 | "screen": "Οθόνη" 91 | } 92 | } 93 | }, 94 | "help": { 95 | "groupName": "Βοήθεια", 96 | "repo": "Αποθετήριο", 97 | "bugs": "Αναφορά σφάλματος" 98 | }, 99 | "tray": { 100 | "toggle": "Εναλλαγή", 101 | "quit": "Κλείσιμο προγράμματος" 102 | }, 103 | "log": { 104 | "listenPort": "Ακρόαση στη θύρα %s.", 105 | "singleInstance": "Μετάβαση στο υπάρχον παράθυρο..." 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /sources/translations/el/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "Δεν είναι δυνατή η σύνδεση στην υπηρεσία Discord.", 4 | "description": "Βεβαιωθείτε ότι είστε συνδεδεμένοι στο διαδίκτυο." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "Περί", 9 | "credits": "Εύσημα", 10 | "licenses": "Άδειες χρήσης" 11 | }, 12 | "about": { 13 | "appDescription": "Discord και Fosscord client με χαρακτηριστικά που εστιάζουν στην προστασία της ιδιωτικής ζωής.", 14 | "appRepo": "Αποθετήριο GitHub", 15 | "featuresTitle": "Σημαίες κατασκευής", 16 | "checksumTitle": "Άθροισμα ελέγχου", 17 | "featuresDefault": "Προκαθορισμένο" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Συγγραφέας, συντηρητής και κύριος προγραμματιστής.", 22 | "contributors": { 23 | "default": "Συνεισφέρων κώδικα." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "showLicense": "Εμφάνιση άδειας", 29 | "thirdPartyLicensesTitle": "Άδειες τρίτων", 30 | "thirdPartyLicensesBody": "Το %s εξαρτάται από το ακόλουθο λογισμικό τρίτων:", 31 | "licensedUnder": "με άδεια %s", 32 | "packageAuthors": "%s συγγραφείς", 33 | "appLicenseTitle": "Άδεια εφαρμογής", 34 | "appLicenseBody": "Το %s είναι ένα ελεύθερο λογισμικό: μπορείτε να το χρησιμοποιήσετε, να το τροποποιήσετε και να το αναδιανείμετε σύμφωνα με τους όρους της άδειας χρήσης του MIT, η οποία θα πρέπει να διανεμηθεί με αυτό το λογισμικό." 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/en/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "tray": { 3 | "toggle": "Toggle", 4 | "quit": "Quit", 5 | "mention": { 6 | "one":"mention", 7 | "other":"mentions", 8 | "zero": "", 9 | "two": "", 10 | "few":"", 11 | "many":"" 12 | } 13 | }, 14 | "context": { 15 | "copy": "Copy", 16 | "paste": "Paste", 17 | "cut": "Cut", 18 | "dictionaryAdd": "Add to the local dictionary", 19 | "copyURL": "Copy link address", 20 | "copyURLText": "Copy link text", 21 | "copyImage": "Copy image", 22 | "copyImageURL": "Copy link to image", 23 | "inspectElement": "Inspect" 24 | }, 25 | "menubar": { 26 | "enabled": "Enabled", 27 | "file": { 28 | "groupName": "File", 29 | "quit": "Quit", 30 | "relaunch": "Relaunch", 31 | "addon": { 32 | "groupName": "Extensions", 33 | "loadNode": "Load node extension", 34 | "loadChrome": "Load Chrome extension" 35 | } 36 | }, 37 | "edit": { 38 | "groupName": "Edit", 39 | "undo": "Undo", 40 | "redo": "Redo", 41 | "selectAll": "Select All" 42 | }, 43 | "view": { 44 | "groupName": "View", 45 | "reload": "Reload", 46 | "forceReload": "Force reload", 47 | "devTools": "Toggle Developer Tools", 48 | "resetZoom": "Actual size", 49 | "zoomIn": "Zoom in", 50 | "zoomOut": "Zoom out", 51 | "fullScreen": "Toggle fullscreen" 52 | }, 53 | "window": { 54 | "groupName": "Window", 55 | "mobileMode": "Hide side bar" 56 | } 57 | }, 58 | "windows": { 59 | "settings": "Settings", 60 | "about": "About", 61 | "docs": "Documentation", 62 | "invite": "Invite" 63 | }, 64 | "dialog": { 65 | "common": { 66 | "error": "Error", 67 | "warning": "Warning", 68 | "continue": "&Continue", 69 | "yes": "&Yes", 70 | "no": "&No", 71 | "source": "Source" 72 | }, 73 | "ver": { 74 | "updateBadge": "[UPDATE]", 75 | "updateTitle": "Update available!", 76 | "update": "New application version is available!", 77 | "recent": "Application is up-to-date!", 78 | "devel": "You're using the non-production build.", 79 | "downgrade": "Your production build is newer than stable!" 80 | }, 81 | "permission": { 82 | "request": { 83 | "denied": "%s: Permission request to %s denied." 84 | }, 85 | "check": { 86 | "denied": "%s: Permission check to %s denied." 87 | }, 88 | "question": { 89 | "title": "Permission request handler", 90 | "message": "Do you want to grant Discord an access to the %s?", 91 | "video": "camera", 92 | "audio": "microphone", 93 | "notifications": "notifications" 94 | } 95 | }, 96 | "mod": { 97 | "nodeExt": "WebCord Node.js Addon", 98 | "crx": "Chrome/Chromium Extension" 99 | }, 100 | "externalApp": { 101 | "title": "Opening link in external app", 102 | "message": "A website tries to redirect the link with the different origin to the external application. Continue anyway?" 103 | }, 104 | "screenShare": { 105 | "close": "Close", 106 | "sound": { 107 | "unavailable": "Sharing audio is unavailable on this platform.", 108 | "system": "Share with device audio." 109 | }, 110 | "source": { 111 | "entire": "Computer's screen", 112 | "screen": "Screen" 113 | } 114 | } 115 | }, 116 | "help": { 117 | "groupName": "Help", 118 | "repo": "Repository", 119 | "bugs": "Report a bug" 120 | }, 121 | "log": { 122 | "singleInstance": "Switching to the existing window...", 123 | "listenPort": "Listening at port %s." 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /sources/translations/en/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "Cannot connect to the Discord service.", 4 | "description": "Please make sure you're connected to the internet." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "About", 9 | "credits": "Credits", 10 | "licenses": "Licenses" 11 | }, 12 | "about": { 13 | "appDescription": "Discord and Fosscord web client with privacy-focused features.", 14 | "appRepo": "GitHub Repository", 15 | "featuresTitle": "Build flags", 16 | "featuresDefault": "Default", 17 | "checksumTitle": "Checksum" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Author, maintainer and main developer.", 22 | "contributors": { 23 | "default": "Code contributor." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "Application license", 29 | "appLicenseBody": "%s is a free software: you can use, modify and redistribute it under terms of MIT license, which should be distributed with this software.", 30 | "showLicense": "Show license", 31 | "thirdPartyLicensesTitle": "Third party licenses", 32 | "thirdPartyLicensesBody": "%s depends on following third party software:", 33 | "licensedUnder": "under %s license", 34 | "packageAuthors": "%s authors" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/es-MX/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "tray": { 3 | "mention": { 4 | "other": "menciones", 5 | "one": "Mencionar" 6 | }, 7 | "quit": "Salir", 8 | "toggle": "Alternar" 9 | }, 10 | "context": { 11 | "copy": "Copiar", 12 | "paste": "Pegar", 13 | "cut": "Cortar", 14 | "dictionaryAdd": "Agregar al diccionario local", 15 | "copyURL": "Copiar direccion de enlace", 16 | "copyURLText": "Copiar texto de enlace", 17 | "copyImage": "Copiar imagen", 18 | "inspectElement": "Inspeccionar", 19 | "copyImageURL": "Copiar el enlace de la imagen" 20 | }, 21 | "menubar": { 22 | "enabled": "Habilitado", 23 | "file": { 24 | "groupName": "Archivo", 25 | "quit": "Salir", 26 | "relaunch": "Reiniciar", 27 | "addon": { 28 | "loadChrome": "Cargar la extensión Chrome", 29 | "loadNode": "Cargar la extensión del nodo", 30 | "groupName": "Extensiones" 31 | } 32 | }, 33 | "edit": { 34 | "groupName": "Editar", 35 | "undo": "Deshacer", 36 | "redo": "Rehacer", 37 | "selectAll": "Seleccionar todo" 38 | }, 39 | "view": { 40 | "groupName": "Vista", 41 | "reload": "Recargar", 42 | "forceReload": "Forzar recarga", 43 | "devTools": "Activar herramientas de desarrollo", 44 | "resetZoom": "Tamaño actual", 45 | "zoomIn": "Acercar", 46 | "zoomOut": "Alejar", 47 | "fullScreen": "Activar pantalla completa" 48 | }, 49 | "window": { 50 | "groupName": "Ventana", 51 | "mobileMode": "Ocultar barra lateral" 52 | } 53 | }, 54 | "windows": { 55 | "settings": "Configuracion", 56 | "about": "Acerca de", 57 | "docs": "Documentación", 58 | "invite": "Invitar" 59 | }, 60 | "dialog": { 61 | "common": { 62 | "error": "Error", 63 | "warning": "Precaución", 64 | "continue": "&Continuar", 65 | "yes": "&Si", 66 | "no": "&No", 67 | "source": "Fuente" 68 | }, 69 | "ver": { 70 | "updateBadge": "[Actualizar]", 71 | "updateTitle": "¡Actualización disponible!", 72 | "update": "¡Una nueva versión de la aplicación está disponible!", 73 | "recent": "¡La aplicación está actualizada!", 74 | "devel": "Estás usando una versión inestable.", 75 | "downgrade": "¡Tu versión de producción es más nueva que la versión estable!" 76 | }, 77 | "permission": { 78 | "request": { 79 | "denied": "%s: La solicitud de permiso para %s se ha denegado." 80 | }, 81 | "check": { 82 | "denied": "%s: La verificación de permiso para %s se ha denegado." 83 | }, 84 | "question": { 85 | "title": "Manejador de solicitud de permisos", 86 | "message": "¿Quieres permitir a discord acceso a %s?", 87 | "audio": "Microfono", 88 | "notifications": "Notificaciones", 89 | "video": "Camara" 90 | } 91 | }, 92 | "mod": { 93 | "nodeExt": "Complemento WebCord Node.js", 94 | "crx": "Extension para Chrome/Chromium" 95 | }, 96 | "externalApp": { 97 | "title": "Abriendo el enlace en una aplicación externa", 98 | "message": "Un sitio web intenta redireccionar el enlace con un origen diferente hacia la aplicación externa. ¿Continuar?" 99 | }, 100 | "screenShare": { 101 | "sound": { 102 | "unavailable": "Compartir el audio no está disponible en esta plataforma.", 103 | "system": "Compartir con dispositivo de audio." 104 | }, 105 | "source": { 106 | "entire": "Pantalla de la computadora", 107 | "screen": "Pantalla" 108 | }, 109 | "close": "Cerrar" 110 | } 111 | }, 112 | "help": { 113 | "groupName": "Ayuda", 114 | "bugs": "Reportar un fallo", 115 | "repo": "Repositorio" 116 | }, 117 | "log": { 118 | "singleInstance": "Cambiando hacia la pantalla actual...", 119 | "listenPort": "Escuchando en el puerto %s." 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sources/translations/es-MX/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "tray": { 4 | "name": "Bandeja", 5 | "description": "Cambia la visibilidad del icono en la bandeja del sistema.", 6 | "labels": { 7 | "disable": "Deshabilitar la función de ocultar la ventana en la bandeja del sistema." 8 | } 9 | }, 10 | "taskbar": { 11 | "labels": { 12 | "flash": "boton con estello en la barra de tareas cuando existen nuevos mensajes." 13 | }, 14 | "description": "Controla el comportamiento de la barra de tareas en la bandeja de WebCord.", 15 | "name": "Barra de tareas" 16 | }, 17 | "menuBar": { 18 | "labels": { 19 | "hide": "oculta la barra de menú (ALT comando cambia automáticamente la visibilidad." 20 | }, 21 | "name": "Barra de menú", 22 | "description": "Cambia la configuración de visibilidad de la barra de menús nativas." 23 | }, 24 | "window": { 25 | "description": "Controla el aspecto y el comportamiento de la ventana que muestra la interfaz de cliente.", 26 | "name": "Ventana principal de WebCord" 27 | }, 28 | "name": "General" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sources/translations/es-MX/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "aboutWindow": { 3 | "about": { 4 | "featuresTitle": "Banderas de compilación", 5 | "featuresDefault": "Predeterminado", 6 | "checksumTitle": "Suma de verificación", 7 | "appRepo": "Repositorio de GitHub", 8 | "appDescription": "Los clientes web de Discord y Fosscord tienen funciones centradas en la privacidad." 9 | }, 10 | "licenses": { 11 | "appLicenseTitle": "Licencia de aplicación", 12 | "showLicense": "Mostrar licencia", 13 | "licensedUnder": "bajo la licencia de %s", 14 | "appLicenseBody": "%s es un software gratuito: puede utilizarlo ,modificarlo y redistribuirlo bajo los términos de la licencia MIT, que debe distribuirse con este software.", 15 | "packageAuthors": "%s autores", 16 | "thirdPartyLicensesTitle": "Licencias de terceros", 17 | "thirdPartyLicensesBody": "%s depende del siguiente software de terceros:" 18 | }, 19 | "credits": { 20 | "people": { 21 | "contributors": { 22 | "default": "Colaborador de código." 23 | }, 24 | "author": "Autor, administrador y desarrollador principal." 25 | } 26 | }, 27 | "nav": { 28 | "about": "Acerca", 29 | "credits": "Créditos", 30 | "licenses": "Licencias" 31 | } 32 | }, 33 | "offline": { 34 | "title": "No se puede conectar al servicio de Discord.", 35 | "description": "Asegúrate de tener conexión a Internet." 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/es/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "No se puede conectar al servicio Discord.", 4 | "description": "Por favor asegurece de estar conectado a internet." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "Acerca de", 9 | "licenses": "Licencias", 10 | "credits": "Creditos" 11 | }, 12 | "about": { 13 | "appDescription": "Cliente web para Discord y Fosscord con características enfocadas en la privacidad.", 14 | "appRepo": "Repositorio de GitHub", 15 | "featuresTitle": "Opciones de compilación", 16 | "featuresDefault": "Predeterminado", 17 | "checksumTitle": "Suma de comprobación" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Autor, mantenedor y desarrollador principal.", 22 | "contributors": { 23 | "default": "Contribuidor de código." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "Licencia de la aplicación", 29 | "appLicenseBody": "%s es un software libre: puedes usarlo, modificarlo y redistribuirlo bajo los términos de la licencia MIT, la cual debe ser distribuída junto con este software.", 30 | "showLicense": "Mostrar licencia", 31 | "thirdPartyLicensesTitle": "Licencias de terceros", 32 | "thirdPartyLicensesBody": "%s depende del siguiente software de terceros:", 33 | "licensedUnder": "bajo la licencia %s", 34 | "packageAuthors": "Autores de %s" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/fr/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": { 3 | "cut": "Couper", 4 | "copy": "Copier", 5 | "copyURL": "Copier l'adresse du lien", 6 | "paste": "Coller", 7 | "dictionaryAdd": "Ajouter au dictionnaire local", 8 | "inspectElement": "Inspecter", 9 | "copyURLText": "Copier le texte du lien", 10 | "copyImage": "Copier l'Image", 11 | "copyImageURL": "Copier le lien de l'Image" 12 | }, 13 | "menubar": { 14 | "enabled": "Activé", 15 | "file": { 16 | "groupName": "Fichier", 17 | "quit": "Quitter", 18 | "relaunch": "Relancer", 19 | "addon": { 20 | "groupName": "Extensions", 21 | "loadNode": "Charger une extension node", 22 | "loadChrome": "Charger une extension Chrome" 23 | } 24 | }, 25 | "edit": { 26 | "groupName": "Édition", 27 | "undo": "Annuler", 28 | "redo": "Rétablir", 29 | "selectAll": "Tout Sélectionner" 30 | }, 31 | "view": { 32 | "groupName": "Affichage", 33 | "reload": "Recharger", 34 | "forceReload": "Forcer le rechargement", 35 | "devTools": "Basculer les outils du développeur", 36 | "resetZoom": "Taille réelle", 37 | "zoomIn": "Zoom avant", 38 | "zoomOut": "Zoom arrière", 39 | "fullScreen": "Basculer en mode plein écran" 40 | }, 41 | "window": { 42 | "groupName": "Fenêtre", 43 | "mobileMode": "Masquer la barre latérale" 44 | } 45 | }, 46 | "tray": { 47 | "quit": "Quitter", 48 | "toggle": "Basculer" 49 | }, 50 | "windows": { 51 | "settings": "Paramètres", 52 | "about": "À propos", 53 | "docs": "Documentation", 54 | "invite": "Inviter" 55 | }, 56 | "dialog": { 57 | "common": { 58 | "error": "Erreur", 59 | "warning": "Attention", 60 | "source": "Source", 61 | "no": "&Non", 62 | "continue": "&Continuer", 63 | "yes": "&Oui" 64 | }, 65 | "ver": { 66 | "updateBadge": "[MISE À JOUR]", 67 | "updateTitle": "Mise à jour disponible !", 68 | "update": "Une nouvelle version de l'application est disponible !", 69 | "devel": "Vous utilisez la version hors production.", 70 | "downgrade": "Votre version de production est plus récente que la version stable !", 71 | "recent": "L'application est à jour !" 72 | }, 73 | "permission": { 74 | "check": { 75 | "denied": "%s : vérification des autorisations pour %s refusée." 76 | }, 77 | "request": { 78 | "denied": "%s : demande de permission à %s refusée." 79 | }, 80 | "question": { 81 | "message": "Voulez-vous accorder à Discord un accès %s ?", 82 | "video": "à la caméra", 83 | "audio": "au micro", 84 | "notifications": "aux notifications", 85 | "title": "Traitement des demandes de permission" 86 | } 87 | }, 88 | "mod": { 89 | "nodeExt": "Module WebCord Node.js", 90 | "crx": "Extension Chrome/Chromium" 91 | }, 92 | "externalApp": { 93 | "title": "Ouverture d'un lien dans une application externe", 94 | "message": "Un site web tente de rediriger le lien avec une origine différente vers l'application externe. Continuer quand même ?" 95 | }, 96 | "screenShare": { 97 | "close": "Fermer", 98 | "sound": { 99 | "system": "Partager avec l'audio du périphérique.", 100 | "unavailable": "Le partage audio n'est pas disponible sur cette plate-forme." 101 | }, 102 | "source": { 103 | "entire": "Écran de l'ordinateur", 104 | "screen": "Écran" 105 | } 106 | } 107 | }, 108 | "help": { 109 | "groupName": "Aide", 110 | "repo": "Dépôt", 111 | "bugs": "Signaler un bug" 112 | }, 113 | "log": { 114 | "singleInstance": "Passage à la fenêtre existante...", 115 | "listenPort": "Écoute du port %s." 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /sources/translations/fr/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "Impossible de se connecter au service Discord.", 4 | "description": "Veuillez vous assurer que vous êtes bien connecté à Internet." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "À propos", 9 | "credits": "Contributeurs", 10 | "licenses": "Licences" 11 | }, 12 | "about": { 13 | "appDescription": "Client Web Discord et Fosscord avec des fonctionnalités axées sur la confidentialité.", 14 | "appRepo": "Dépôt GitHub", 15 | "featuresTitle": "Paramètres de construction (Build flags)", 16 | "featuresDefault": "Par défaut", 17 | "checksumTitle": "Somme de contrôle (Checksum)" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Auteur, mainteneur et développeur principal.", 22 | "contributors": { 23 | "default": "Contributeur du code." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "Licence d'application", 29 | "appLicenseBody": "%s est un logiciel libre : vous pouvez l'utiliser, le modifier et le redistribuer selon les termes de la licence MIT, qui devrait être distribuée avec ce logiciel.", 30 | "showLicense": "Afficher la licence", 31 | "thirdPartyLicensesTitle": "Licences tierces", 32 | "thirdPartyLicensesBody": "%s dépend des logiciels tiers suivants :", 33 | "licensedUnder": "sous licence %s", 34 | "packageAuthors": "Auteurs de %s" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/it/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "menubar": { 3 | "file": { 4 | "addon": { 5 | "loadChrome": "Carica estensione di Chrome", 6 | "groupName": "Estensioni", 7 | "loadNode": "Carica estensione di node" 8 | }, 9 | "quit": "Chiudi", 10 | "relaunch": "Rilancia", 11 | "groupName": "File" 12 | }, 13 | "window": { 14 | "groupName": "Finestra", 15 | "mobileMode": "Nascondi barra laterale" 16 | }, 17 | "enabled": "Abilitato", 18 | "edit": { 19 | "groupName": "Modifica", 20 | "undo": "Annulla", 21 | "redo": "Ripeti" 22 | }, 23 | "view": { 24 | "groupName": "Visualizza", 25 | "reload": "Ricarica", 26 | "forceReload": "Ricarica forzatamente", 27 | "devTools": "Mostra Strumenti per Sviluppatori", 28 | "resetZoom": "Dimensione effettiva", 29 | "zoomOut": "Riduci", 30 | "zoomIn": "Ingrandisci", 31 | "fullScreen": "Passa a schermo intero" 32 | } 33 | }, 34 | "dialog": { 35 | "mod": { 36 | "crx": "Estensione Chrome/Chromium", 37 | "nodeExt": "Addon WebCord Node.js" 38 | }, 39 | "screenShare": { 40 | "close": "Chiudi", 41 | "source": { 42 | "screen": "Schermo", 43 | "entire": "Schermo del computer" 44 | }, 45 | "sound": { 46 | "system": "Condividi con l'audio del dispositivo.", 47 | "unavailable": "La condivisione dell'audio non è disponibile su questa piattaforma." 48 | } 49 | }, 50 | "common": { 51 | "error": "Errore", 52 | "warning": "Attenzione", 53 | "source": "Sorgente", 54 | "continue": "&Continua", 55 | "no": "&No", 56 | "yes": "&Si" 57 | }, 58 | "ver": { 59 | "updateBadge": "[AGGIORNAMENTO]", 60 | "downgrade": "La build di produzione attuale è più recente di quella stabile!", 61 | "update": "Una nuova versione dell'applicazione è disponibile!", 62 | "recent": "L'applicazione è aggiornata!", 63 | "devel": "Non stai usando la build di produzione.", 64 | "updateTitle": "Aggiornamento disponibile!" 65 | }, 66 | "permission": { 67 | "check": { 68 | "denied": "%s: Ho fallito il controllo dei permessi su %s." 69 | }, 70 | "question": { 71 | "notifications": "notifiche", 72 | "title": "Gestore di richiesta dei permessi", 73 | "message": "Vuoi permettere a Discord di accedere al file %s?", 74 | "video": "fotocamera", 75 | "audio": "microfono" 76 | }, 77 | "request": { 78 | "denied": "%s: Ho negato il permesso richiesto su %s." 79 | } 80 | }, 81 | "externalApp": { 82 | "title": "Aprire link in app esterna", 83 | "message": "Un sito web sta cercando di redirezionare il link con origine differente all'apllicazione esterna. Continuo comunque?" 84 | } 85 | }, 86 | "context": { 87 | "paste": "Incolla", 88 | "dictionaryAdd": "Aggiungi al dizionario locale", 89 | "copyURLText": "Copia testo dell'indirizzo", 90 | "copy": "Copia", 91 | "cut": "Taglia", 92 | "copyImageURL": "Copia link all'immagine", 93 | "copyURL": "Copia indirizzo", 94 | "copyImage": "Copia immagine", 95 | "inspectElement": "Ispeziona" 96 | }, 97 | "help": { 98 | "groupName": "Aiuto", 99 | "repo": "Repository", 100 | "bugs": "Segnala un bug" 101 | }, 102 | "log": { 103 | "singleInstance": "Passo alla finestra esistente...", 104 | "listenPort": "Ascolto su porta %s." 105 | }, 106 | "tray": { 107 | "toggle": "Mostra", 108 | "quit": "Esci" 109 | }, 110 | "windows": { 111 | "settings": "Impostazioni", 112 | "docs": "Documentazione", 113 | "about": "Riguardo a", 114 | "invite": "Invito" 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /sources/translations/it/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "description": "Verifica di essere connesso ad internet.", 4 | "title": "Impossibile collegarsi al servizio di Discord." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "credits": "Crediti", 9 | "licenses": "Licenze", 10 | "about": "Riguardo a" 11 | }, 12 | "about": { 13 | "featuresDefault": "Default", 14 | "checksumTitle": "Checksum", 15 | "appDescription": "Client web per Discord e Fosscord con features orientate alla privacy.", 16 | "featuresTitle": "Build flags", 17 | "appRepo": "Repository GitHub" 18 | }, 19 | "licenses": { 20 | "appLicenseTitle": "Licenza dell'applicazione", 21 | "thirdPartyLicensesBody": "%s dipende dai software di terze parti seguenti:", 22 | "appLicenseBody": "%s è un software gratuito: puoi usarlo, modificarlo e ridistribuirlo sotto termini di licenza MIT, che dovrebbe essere distribuita insieme a questo software.", 23 | "packageAuthors": "autori %s", 24 | "licensedUnder": "sotto licenza %s", 25 | "showLicense": "Mostra licenza", 26 | "thirdPartyLicensesTitle": "Licenze di terze parti" 27 | }, 28 | "credits": { 29 | "people": { 30 | "author": "Autore, manutentore e sviluppatore principale.", 31 | "contributors": { 32 | "default": "Contributore di codice." 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/ja/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": { 3 | "copyURLText": "リンクをテキストとしてコピー", 4 | "cut": "カット", 5 | "dictionaryAdd": "ローカルの辞書に登録", 6 | "inspectElement": "検証", 7 | "copy": "コピー", 8 | "paste": "ペースト", 9 | "copyURL": "URLのコピー", 10 | "copyImage": "画像のコピー", 11 | "copyImageURL": "画像へのリンクをコピー" 12 | }, 13 | "tray": { 14 | "toggle": "トグル", 15 | "quit": "終了", 16 | "mention": { 17 | "one": "言及", 18 | "other": "言及" 19 | } 20 | }, 21 | "windows": { 22 | "settings": "設定", 23 | "about": "アプリについて", 24 | "docs": "ドキュメント", 25 | "invite": "招待" 26 | }, 27 | "dialog": { 28 | "common": { 29 | "error": "エラー", 30 | "warning": "警告", 31 | "continue": "&次へ", 32 | "yes": "&はい", 33 | "no": "&いいえ", 34 | "source": "ソース" 35 | }, 36 | "ver": { 37 | "updateBadge": "[アップデート]", 38 | "updateTitle": "アップデートがあります!", 39 | "recent": "アプリケーションは最新です!", 40 | "update": "アプリケーションの新しいバージョンがあります!", 41 | "devel": "プロダクションバージョンではないビルドを利用しています。", 42 | "downgrade": "このプロダクションバージョンはstableよりも新しいです!" 43 | }, 44 | "permission": { 45 | "request": { 46 | "denied": "%s: %sの権限リクエストが拒否されました。" 47 | }, 48 | "check": { 49 | "denied": "%s: %sの権限チェックが拒否されました。" 50 | }, 51 | "question": { 52 | "title": "権限リクエストハンドラ", 53 | "message": "Discordに%sへのアクセス権限を付与しますか?", 54 | "video": "カメラ", 55 | "audio": "マイク", 56 | "notifications": "通知" 57 | } 58 | }, 59 | "screenShare": { 60 | "source": { 61 | "entire": "コンピュータの画面", 62 | "screen": "画面" 63 | }, 64 | "sound": { 65 | "unavailable": "音声を共有する機能はこのプラットフォームには用意されていません。", 66 | "system": "デバイスの音声を共有。" 67 | }, 68 | "close": "閉じる" 69 | }, 70 | "externalApp": { 71 | "message": "ウェブサイトは別のオリジンのリンクを外部アプリケーションにリダイレクトしようとします。 続けます?", 72 | "title": "リンクを外部アプリで開きます" 73 | }, 74 | "mod": { 75 | "nodeExt": "WebCord Node.js拡張", 76 | "crx": "Chrome/Chromium拡張" 77 | } 78 | }, 79 | "help": { 80 | "groupName": "ヘルプ", 81 | "repo": "リポジトリ", 82 | "bugs": "バグレポート" 83 | }, 84 | "log": { 85 | "singleInstance": "すでに開かれているウインドウに切り替えます...", 86 | "listenPort": "ポート%sにてListenしています。" 87 | }, 88 | "menubar": { 89 | "enabled": "有効", 90 | "file": { 91 | "groupName": "ファイル", 92 | "quit": "終了", 93 | "relaunch": "再起動", 94 | "addon": { 95 | "loadChrome": "Chrome拡張の読み込み", 96 | "groupName": "拡張機能", 97 | "loadNode": "Node拡張の読み込み" 98 | } 99 | }, 100 | "view": { 101 | "groupName": "表示", 102 | "devTools": "デベロッパーツール", 103 | "resetZoom": "実際のサイズ", 104 | "zoomIn": "ズームイン", 105 | "reload": "再読込", 106 | "forceReload": "強制再読込", 107 | "fullScreen": "フルスクリーン", 108 | "zoomOut": "ズームアウト" 109 | }, 110 | "window": { 111 | "mobileMode": "モバイルモード", 112 | "groupName": "ウインドウ" 113 | }, 114 | "edit": { 115 | "groupName": "編集", 116 | "undo": "もとに戻す", 117 | "redo": "やり直し", 118 | "selectAll": "すべて選択" 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sources/translations/ja/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "name": "一般", 4 | "menuBar": { 5 | "name": "メニューバー", 6 | "description": "メニューバーの表示を設定します。", 7 | "labels": { 8 | "hide": "メニューバーを自動的に非表示にする (ALTで表示を切り替えられます)。" 9 | } 10 | }, 11 | "window": { 12 | "description": "クライアントのウインドウの見た目や機能の設定。", 13 | "name": "WebCordのメインウインドウ", 14 | "labels": { 15 | "transparent": "透過バックグラウンドを許可。", 16 | "hideOnClose": "閉じるボタンでウインドウをトレイに隠す。" 17 | } 18 | }, 19 | "tray": { 20 | "name": "トレイ", 21 | "description": "システムトレイのアイコンの表示について設定します。", 22 | "labels": { 23 | "disable": "ウインドウをシステムトレイに隠す機能を無効化。" 24 | } 25 | }, 26 | "taskbar": { 27 | "name": "タスクバー", 28 | "description": "WebCordのタスクバーエントリの機能について設定。", 29 | "labels": { 30 | "flash": "新しいメッセージが来た際にタスクバーボタンの点滅。" 31 | } 32 | } 33 | }, 34 | "privacy": { 35 | "name": "プライバシー", 36 | "blockApi": { 37 | "name": "Discord APIブロック", 38 | "description": "DiscordのAPIをブロックすることで更にプライバシーを強化できます。", 39 | "labels": { 40 | "typingIndicator": "入力中インジケータをブロック (/typing)。", 41 | "fingerprinting": "不明な機種特定機能のブロック (api.js and cdn-cgi)。", 42 | "science": "不明なテレメトリーのエンドポイントをブロック (/science/tracing)。" 43 | } 44 | }, 45 | "permissions": { 46 | "name": "権限", 47 | "description": "Discordのウェブサイトから要求される権限を許可、または拒否します。こちらに表示されていない権限は自動的にブロックされています。", 48 | "labels": { 49 | "video": "カメラ", 50 | "audio": "マイク", 51 | "fullscreen": "全画面表示", 52 | "display-capture": "画面キャプチャ", 53 | "notifications": "通知", 54 | "background-sync": "バックグラウンド同期", 55 | "speaker-selection": "オーディオデバイスのリスト" 56 | } 57 | } 58 | }, 59 | "advanced": { 60 | "csp": { 61 | "name": "コンテンツセキュリティポリシー", 62 | "description": "プライバシーやセキュリティの観点からDiscordから設定されたContent Securityヘッダーを無視しクライアントで独自のヘッダーを設定する。", 63 | "labels": { 64 | "enabled": "ビルトインのContent Securityポリシーを使用する。" 65 | } 66 | }, 67 | "unix": { 68 | "description": "Chromium/Blinkエンジンの実験的フラグがついている機能の有効化。自己責任でお使いください。", 69 | "name": "Unix/UnixのようなOS向けの実験的機能", 70 | "labels": { 71 | "autoscroll": "中クリックスクロールの有効化 (aka. 自動スクロール)。" 72 | } 73 | }, 74 | "currentInstance": { 75 | "name": "Discordのインスタンス", 76 | "description": "WebCordの接続するインスタンスを選択します。 実験的 FossCordの実装は完了していません." 77 | }, 78 | "devel": { 79 | "name": "開発者モード", 80 | "description": "開発中や壊れた、もしくは非推奨な機能へのアクセスを許可する。 注意: アプリケーションのメンテーナーはこのオプションが有効化されたあとで起きたバグや問題について一切の責任を負いません。問題を報告しないでください!", 81 | "labels": { 82 | "enabled": "開発者モードの有効化。" 83 | } 84 | }, 85 | "optimize": { 86 | "name": "クライアント最適化", 87 | "labels": { 88 | "gpu": "現在アクティブなグラフィックカードに対してクライアントを最適化。" 89 | }, 90 | "description": " Electronのパフォーマンスや機能性を向上させる可能性のあるカスタムフラグをChromiumエンジンに適用するために、ユーザーソフトウェアやハードウェアを検出しようとします。実験的 Chromium がデフォルトでこれらのフラグを適用しない理由があるためです。" 91 | }, 92 | "webApi": { 93 | "name": "JavaScript APIへのアクセス", 94 | "labels": { 95 | "webGl": "WebGLサポートの有効化。" 96 | }, 97 | "description": "非特権(DOM)スクリプトで許可されるAPIを制御します。" 98 | }, 99 | "cspThirdParty": { 100 | "name": "サードパーティーのウェブサイト", 101 | "description": "接続またはコンテンツを表示しても良いサードパーティーのウェブサイトのリストを設定。", 102 | "labels": { 103 | "gif": "GIFプロバイダ" 104 | }, 105 | "info": { 106 | "googleStorageApi": "Discordにファイルをアップロードするには必要です!" 107 | } 108 | }, 109 | "redirection": { 110 | "name": "クロスオリジンのリダイレクトの挙動", 111 | "description": "クロスオリジンのリダイレクトの挙動について設定します。これはアプリケーションのセキュリティに影響します。", 112 | "labels": { 113 | "warn": "外部のウェブサイトへのクロスオリジンのリダイレクトについて常にユーザーを警告する。" 114 | } 115 | }, 116 | "name": "高度な設定" 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /sources/translations/ja/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "aboutWindow": { 3 | "licenses": { 4 | "thirdPartyLicensesBody": "%sは次のサードパーティライセンスに依存しています:", 5 | "appLicenseTitle": "アプリケーションライセンス", 6 | "showLicense": "ライセンスを表示", 7 | "thirdPartyLicensesTitle": "サードパーティーライセンス", 8 | "appLicenseBody": "%sはフリーなソフトウェアです。MITライセンスのもとで自由に使用、改変そして再配布することができます。再配布の際にはライセンスも含める必要があります。", 9 | "licensedUnder": "%s ライセンス", 10 | "packageAuthors": "%s 作者" 11 | }, 12 | "nav": { 13 | "about": "アプリについて", 14 | "credits": "クレジット", 15 | "licenses": "ライセンス" 16 | }, 17 | "about": { 18 | "appDescription": "DiscordとFosscordのプライバシー重視のウェブクライアント。", 19 | "appRepo": "GitHubリポジトリ", 20 | "featuresTitle": "ビルドフラッグ", 21 | "featuresDefault": "デフォルト", 22 | "checksumTitle": "チェックサム" 23 | }, 24 | "credits": { 25 | "people": { 26 | "author": "作者、メンテーナーとメイン開発者。", 27 | "contributors": { 28 | "default": "コントリビューター。" 29 | } 30 | } 31 | } 32 | }, 33 | "offline": { 34 | "title": "Discordのサーバーに接続できません。", 35 | "description": "インターネットに接続されていることを確認してください。" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/nb/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "menubar": { 3 | "view": { 4 | "reload": "Last inn igjen", 5 | "devTools": "Veksle utviklerverktøy", 6 | "resetZoom": "Faktisk størrelse", 7 | "groupName": "Vis", 8 | "zoomIn": "Forstørr", 9 | "forceReload": "Påtving gjeninnlasting", 10 | "zoomOut": "Forminsk", 11 | "fullScreen": "Veksle fullskjermsvisning" 12 | }, 13 | "file": { 14 | "quit": "Avslutt", 15 | "addon": { 16 | "loadNode": "Last inn node-utvidelse", 17 | "loadChrome": "Last inn Chrome-utvidelse", 18 | "groupName": "Utvidelser" 19 | }, 20 | "groupName": "Fil", 21 | "relaunch": "Start igjen" 22 | }, 23 | "edit": { 24 | "undo": "Angre", 25 | "groupName": "Rediger", 26 | "redo": "Gjenta" 27 | }, 28 | "enabled": "Påskrudd", 29 | "window": { 30 | "groupName": "Vindu", 31 | "mobileMode": "Skjul sidefelt" 32 | } 33 | }, 34 | "tray": { 35 | "toggle": "Veksle", 36 | "quit": "Avslutt" 37 | }, 38 | "context": { 39 | "paste": "Lim inn", 40 | "cut": "Klipp ut", 41 | "copy": "Kopier", 42 | "dictionaryAdd": "Legg til i lokal ordliste", 43 | "copyURL": "Kopier lenkeadresse", 44 | "copyURLText": "Kopier lenketekst", 45 | "inspectElement": "Inspiser" 46 | }, 47 | "dialog": { 48 | "common": { 49 | "warning": "Advarsel", 50 | "continue": "&Fortsett", 51 | "yes": "&Ja", 52 | "no": "&Nei", 53 | "source": "Kilde", 54 | "error": "Feil" 55 | }, 56 | "ver": { 57 | "updateTitle": "Oppgradering tilgjengelig!", 58 | "update": "Ny programversjon tilgjengelig!", 59 | "recent": "Nyeste utgave av programmet er i bruk!", 60 | "devel": "Du bruker et utviklingsbygg.", 61 | "downgrade": "Din versjon er nyere enn den stabile versjonen!", 62 | "updateBadge": "[OPPGRADER]" 63 | }, 64 | "permission": { 65 | "check": { 66 | "denied": "%s: Tilgangssjekk for %s ble avslått." 67 | }, 68 | "request": { 69 | "denied": "%s: Tilgangsforespørsel til %s ble ikke innvilget." 70 | } 71 | }, 72 | "mod": { 73 | "nodeExt": "WebCord-Node.js-tillegg", 74 | "crx": "Chrome/Chromium-utvidelse" 75 | }, 76 | "externalApp": { 77 | "message": "La en nettside videresende lenken med forskjellig opphav til det eksterne programmet?", 78 | "title": "Åpner lenke i eksternt program" 79 | } 80 | }, 81 | "help": { 82 | "groupName": "Hjelp", 83 | "bugs": "Rapporter en feil", 84 | "repo": "Kodelager" 85 | }, 86 | "log": { 87 | "listenPort": "Lytter til port %s.", 88 | "singleInstance": "Bytter til det eksisterende vinduet …" 89 | }, 90 | "windows": { 91 | "invite": "Invitasjon", 92 | "about": "Om", 93 | "docs": "Dokumentasjon", 94 | "settings": "Innstillinger" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /sources/translations/nb/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /sources/translations/nb/web.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /sources/translations/nl/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "tray": { 3 | "toggle": "Schakel", 4 | "quit": "Afsluiten", 5 | "mention": { 6 | "one": "Benoem" 7 | } 8 | }, 9 | "menubar": { 10 | "file": { 11 | "quit": "Afsluiten", 12 | "relaunch": "Heropstarten", 13 | "groupName": "Bestand", 14 | "addon": { 15 | "loadChrome": "Laad Chrome extensie", 16 | "loadNode": "Laad node extensie", 17 | "groupName": "Extensies" 18 | } 19 | }, 20 | "edit": { 21 | "selectAll": "Selecteer Alles", 22 | "undo": "Ongedaan maken", 23 | "redo": "Opnieuw doen", 24 | "groupName": "Bewerken" 25 | }, 26 | "view": { 27 | "zoomOut": "Uitzoomen", 28 | "groupName": "Bekijk", 29 | "reload": "Herladen", 30 | "resetZoom": "Daadwerkelijke grootte", 31 | "zoomIn": "In zoomen", 32 | "fullScreen": "Volledig scherm activeren", 33 | "devTools": "Schakel ontwikkelaarstools in/uit", 34 | "forceReload": "Forceer herladen" 35 | }, 36 | "window": { 37 | "groupName": "Venster", 38 | "mobileMode": "Verberg de zijbalk" 39 | }, 40 | "enabled": "Ingeschakeld" 41 | }, 42 | "context": { 43 | "paste": "Plakken", 44 | "copyImage": "Kopieer afbeelding", 45 | "cut": "Knip", 46 | "copyURLText": "Kopieer linktekst", 47 | "copyURL": "Kopieer link adres", 48 | "copyImageURL": "Kopieer link naar afbeelding", 49 | "dictionaryAdd": "Voeg toe aan het lokale woordenboek", 50 | "copy": "kopieer", 51 | "inspectElement": "Inspecteren" 52 | }, 53 | "dialog": { 54 | "permission": { 55 | "question": { 56 | "notifications": "meldingen", 57 | "message": "Wil je Discord toegang verlenen tot de %s?", 58 | "title": "Behandelaar van toestemmingsverzoeken", 59 | "audio": "microfoon", 60 | "video": "camera" 61 | }, 62 | "request": { 63 | "denied": "%s: Toestemmingsverzoek aan %s afgewezen." 64 | }, 65 | "check": { 66 | "denied": "%s: Toestemmingscontrole voor %s geweigerd." 67 | } 68 | }, 69 | "screenShare": { 70 | "close": "Sluiten", 71 | "source": { 72 | "screen": "Scherm", 73 | "entire": "Computer scherm" 74 | }, 75 | "sound": { 76 | "unavailable": "Het delen van audio is niet beschikbaar op dit platform.", 77 | "system": "Deel met apparaataudio." 78 | } 79 | }, 80 | "ver": { 81 | "downgrade": "Uw productiebuild is nieuwer dan stabiel!", 82 | "update": "Er is een nieuwe applicatieversie beschikbaar!", 83 | "recent": "Applicatie is up-to-date!", 84 | "updateBadge": "[UPDATE]", 85 | "updateTitle": "Update beschikbaar!", 86 | "devel": "U gebruikt de niet-productiebuild." 87 | }, 88 | "externalApp": { 89 | "message": "Een website probeert de link met de andere oorsprong om te leiden naar de externe applicatie. Ga toch verder?", 90 | "title": "Link openen in externe app" 91 | }, 92 | "common": { 93 | "continue": "&Doorgaan", 94 | "no": "&Nee", 95 | "yes": "&Ja", 96 | "source": "Bron", 97 | "warning": "Waarschuwing", 98 | "error": "Fout" 99 | }, 100 | "mod": { 101 | "nodeExt": "WebCord Node.js-add-on", 102 | "crx": "Chrome/Chromium-extensie" 103 | } 104 | }, 105 | "windows": { 106 | "settings": "Instellingen", 107 | "about": "Over", 108 | "docs": "Documentatie", 109 | "invite": "Inviteer" 110 | }, 111 | "log": { 112 | "singleInstance": "Overschakelen naar het bestaande venster...", 113 | "listenPort": "Luisteren op poort %s." 114 | }, 115 | "help": { 116 | "groupName": "Help", 117 | "bugs": "Rapporteer een bug", 118 | "repo": "Opslagplaats" 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /sources/translations/nl/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "tray": { 4 | "name": "Tray", 5 | "description": "Verandert de zichtbaarheid van het pictogram in het systeemvak.", 6 | "labels": { 7 | "disable": "Schakel het verbergen van venster naar de systeemvakfunctionaliteit uit." 8 | } 9 | }, 10 | "taskbar": { 11 | "name": "Taakbalk" 12 | }, 13 | "menuBar": { 14 | "labels": { 15 | "hide": "Verberg de menubalk automatisch (de ALT-toets wijzigt de zichtbaarheid)." 16 | }, 17 | "name": "Menubalk", 18 | "description": "Wijzigt de zichtbaarheidsinstellingen van de eigen menubalk." 19 | }, 20 | "name": "Algemeen" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sources/translations/nl/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "aboutWindow": { 3 | "licenses": { 4 | "appLicenseTitle": "Applicatielicentie", 5 | "showLicense": "Toon licentie", 6 | "licensedUnder": "onder %s licentie", 7 | "appLicenseBody": "%s is gratis software: u kunt het gebruiken, wijzigen en herdistribueren onder de voorwaarden van de MIT-licentie, die bij deze software moet worden gedistribueerd.", 8 | "packageAuthors": "%s auteurs", 9 | "thirdPartyLicensesTitle": "Derde partij licenties", 10 | "thirdPartyLicensesBody": "%s is afhankelijk van de volgende derde partij software:" 11 | }, 12 | "about": { 13 | "featuresDefault": "Standaard", 14 | "checksumTitle": "Checksum", 15 | "appRepo": "GitHub-opslagplaats", 16 | "appDescription": "Discord- en Fosscord-webclient met op privacy gerichte functies." 17 | }, 18 | "credits": { 19 | "people": { 20 | "contributors": { 21 | "default": "Codebijdrager." 22 | }, 23 | "author": "Auteur, onderhouder en hoofdontwikkelaar." 24 | } 25 | }, 26 | "nav": { 27 | "about": "Over", 28 | "credits": "Crediteringen", 29 | "licenses": "Licenties" 30 | } 31 | }, 32 | "offline": { 33 | "title": "Kan niet connecteren met de Discord dienst.", 34 | "description": "Zorg ervoor dat u verbonden bent met internet." 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sources/translations/pl/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "tray": { 3 | "toggle": "Przełącz", 4 | "quit": "Zakończ", 5 | "mention": { 6 | "one":"wzmianka", 7 | "few":"wzmianki", 8 | "many":"wzmianek", 9 | "other":"wzmianek" 10 | } 11 | }, 12 | "context": { 13 | "copy": "Kopiuj", 14 | "paste": "Wklej", 15 | "cut": "Wytnij", 16 | "dictionaryAdd": "Dodaj do słownika", 17 | "copyURL": "Kopiuj adres linku", 18 | "copyURLText": "Kopiuj tekst linku", 19 | "copyImage": "Kopiuj obraz", 20 | "copyImageURL": "Kopiuj odnośnik do obrazu", 21 | "inspectElement": "Zbadaj" 22 | }, 23 | "menubar": { 24 | "enabled": "Włączony", 25 | "file": { 26 | "groupName": "Plik", 27 | "quit": "Zakończ", 28 | "relaunch": "Uruchom ponownie", 29 | "addon": { 30 | "groupName": "Rozszerzenia", 31 | "loadNode": "Załaduj dodatek Node.js", 32 | "loadChrome": "Załaduj dodatek Chrome" 33 | } 34 | }, 35 | "edit": { 36 | "groupName": "Edycja", 37 | "undo": "Cofnij", 38 | "redo": "Ponów", 39 | "selectAll": "Zaznacz wszystko" 40 | }, 41 | "view": { 42 | "groupName": "Widok", 43 | "reload": "Odśwież", 44 | "forceReload": "Wymuś odświeżanie", 45 | "devTools": "Narzędzia deweloperskie", 46 | "resetZoom": "Resetuj rozmiar", 47 | "zoomIn": "Powiększ", 48 | "zoomOut": "Pomniejsz", 49 | "fullScreen": "Przełącz pełny ekran" 50 | }, 51 | "window": { 52 | "groupName": "Okno", 53 | "mobileMode": "Ukryj pasek boczny" 54 | } 55 | }, 56 | "windows": { 57 | "about": "O aplikacji", 58 | "settings": "Ustawienia", 59 | "docs": "Dokumentacja", 60 | "invite": "Zaproszenie" 61 | }, 62 | "dialog": { 63 | "common": { 64 | "error": "Błąd", 65 | "warning": "Uwaga", 66 | "continue": "&Kontynuuj", 67 | "yes": "&Tak", 68 | "no": "&Nie", 69 | "source": "Źródło" 70 | }, 71 | "ver": { 72 | "updateBadge": "[ZMIANY]", 73 | "updateTitle": "Dostęna aktualizacja!", 74 | "update": "Dostępna nowa wersja programu!", 75 | "recent": "Aplikacja jest w najnowszej wersji!", 76 | "devel": "Korzystasz z wersji rozwojowej aplikacji.", 77 | "downgrade": "Wersja produkcyjna aplikacji jest nowsza niż stabilna!" 78 | }, 79 | "permission": { 80 | "request": { 81 | "denied": "%s: Żądanie uprawnień do elementu „%s” odrzucone." 82 | }, 83 | "check": { 84 | "denied": "%s: Sprawdzenie uprawnień do elementu „%s” odrzucone." 85 | }, 86 | "question": { 87 | "title": "Obsługa żądań uprawnień", 88 | "message": "Czy chcesz pozwolić Discordowi na dostęp do %s?", 89 | "audio": "mikrofonu", 90 | "video": "kamery", 91 | "notifications": "powiadomień" 92 | } 93 | }, 94 | "mod": { 95 | "nodeExt": "Dodatek Node.js WebCorda", 96 | "crx": "Rozszerzenie Chrome/Chromium" 97 | }, 98 | "externalApp": { 99 | "title": "Otwieranie linku w programie zewnętrznym", 100 | "message": "Strona usiłuje przekierować link o innym źródle pochodzenia do aplikacji zewnętrznej. Kontynuować mimo tego?" 101 | }, 102 | "screenShare": { 103 | "close": "Zamknij", 104 | "sound": { 105 | "unavailable": "Udostępnianie dźwięku nie jest dostępne na tej platformie.", 106 | "system": "Udostępnij z dźwiękiem urządzenia." 107 | }, 108 | "source": { 109 | "entire": "Ekran komputera", 110 | "screen": "Ekran" 111 | } 112 | } 113 | }, 114 | "help": { 115 | "groupName": "Pomoc", 116 | "repo": "Repozytorium", 117 | "bugs": "Zgłoś błąd" 118 | }, 119 | "log": { 120 | "singleInstance": "Przełączanie do istniejącego okna...", 121 | "listenPort": "Nasłuchiwanie na porcie: %s." 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /sources/translations/pl/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "Nie można połączyć z usługą Discord.", 4 | "description": "Proszę sprawdzić połączenie z internetem." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "O aplikacji", 9 | "credits": "Zasługi", 10 | "licenses": "Licencje" 11 | }, 12 | "about": { 13 | "appDescription": "Klient dla usług Discord i Fosscord z funkcjami skupionymi na prywatności.", 14 | "appRepo": "Repozytorium GitHub", 15 | "featuresTitle": "Flagi budowania", 16 | "featuresDefault": "Domyślne", 17 | "checksumTitle": "Suma kontrolna" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Autor, opiekun i główny deweloper aplikacji.", 22 | "contributors": { 23 | "default": "Współtwórca kodu." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "Licencja aplikacji", 29 | "appLicenseBody": "%s jest wolnym oprogramowaniem: możesz swobodnie modyfikować i rozpowszechniać to oprogramowanie zgodnie z warunkami licencji MIT, która powinna być dostarczona wraz z tym oprogramowaniem.", 30 | "showLicense": "Wyświetl licencję", 31 | "thirdPartyLicensesTitle": "Licencje firm trzecich", 32 | "thirdPartyLicensesBody": "%s jest zależny od następującego oprogramowania firm trzecich:", 33 | "licensedUnder": "na licencji %s", 34 | "packageAuthors": "autorzy %s" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/pt/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "tray": { 3 | "quit": "Sair", 4 | "toggle": "Alternar" 5 | }, 6 | "context": { 7 | "copy": "Copiar", 8 | "paste": "Colar", 9 | "cut": "Cortar", 10 | "dictionaryAdd": "Adicionar ao dicionário local", 11 | "copyURL": "Copiar link", 12 | "copyURLText": "Copiar o texto do link", 13 | "inspectElement": "Inspecionar" 14 | }, 15 | "menubar": { 16 | "file": { 17 | "groupName": "Ficheiro", 18 | "quit": "Sair", 19 | "relaunch": "Reiniciar", 20 | "addon": { 21 | "groupName": "Extensões", 22 | "loadNode": "Carregar extensão node", 23 | "loadChrome": "Carregar extensão Chrome" 24 | } 25 | }, 26 | "view": { 27 | "reload": "Recarregar", 28 | "groupName": "Ver", 29 | "forceReload": "Forçar recarregamento", 30 | "devTools": "Alternar ferramentas de desenvolvedor", 31 | "resetZoom": "Tamanho atual", 32 | "zoomIn": "Mais zoom", 33 | "zoomOut": "Menos zoom", 34 | "fullScreen": "Alternar tela cheia" 35 | }, 36 | "window": { 37 | "groupName": "Janela", 38 | "mobileMode": "Ocultar barra lateral" 39 | }, 40 | "enabled": "Ativado", 41 | "edit": { 42 | "groupName": "Editar", 43 | "undo": "Desfazer", 44 | "redo": "Refazer" 45 | } 46 | }, 47 | "dialog": { 48 | "common": { 49 | "source": "Fonte", 50 | "error": "Erro", 51 | "warning": "Aviso", 52 | "continue": "&Continuar", 53 | "yes": "&Sim", 54 | "no": "&Não" 55 | }, 56 | "ver": { 57 | "update": "Novo versão do aplicativo está disponível!", 58 | "updateTitle": "Atualização disponível!", 59 | "recent": "Aplicativo já está na ultima versão!", 60 | "devel": "Você está utilizando uma versão de desenvolvimento.", 61 | "updateBadge": "[ATUALIZAR]", 62 | "downgrade": "Sua versão é mais recente que a estavel!" 63 | }, 64 | "permission": { 65 | "request": { 66 | "denied": "%s: Permissão para %s: negada." 67 | }, 68 | "check": { 69 | "denied": "%s: Verificação de permissão para %s negada." 70 | } 71 | }, 72 | "mod": { 73 | "nodeExt": "Complemento Node.js para o Webcord", 74 | "crx": "Extensão Chrome/Chromium" 75 | }, 76 | "externalApp": { 77 | "title": "Abrindo link em um aplicativo externo", 78 | "message": "Um site tentou redirecionar o link para uma aplicação externa. Continuar mesmo assim?" 79 | }, 80 | "screenShare": { 81 | "close": "Fechar", 82 | "sound": { 83 | "unavailable": "Compartilhamento de audio não disponível nessa plataforma.", 84 | "system": "Compartilhar com dispositivo de audio." 85 | }, 86 | "source": { 87 | "entire": "Tela do computador", 88 | "screen": "Tela" 89 | } 90 | } 91 | }, 92 | "windows": { 93 | "settings": "Configurações", 94 | "about": "Sobre", 95 | "docs": "Documentação", 96 | "invite": "Convidar" 97 | }, 98 | "help": { 99 | "groupName": "Ajuda", 100 | "repo": "Repositório", 101 | "bugs": "Reportar um bug" 102 | }, 103 | "log": { 104 | "singleInstance": "Mudando para uma janela existente...", 105 | "listenPort": "Escutando na porta %s." 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /sources/translations/pt/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "privacy": { 3 | "permissions": { 4 | "name": "Permissões", 5 | "labels": { 6 | "audio": "Microfone", 7 | "notifications": "Notificações" 8 | } 9 | }, 10 | "name": "Privacidade" 11 | }, 12 | "general": { 13 | "name": "Geral", 14 | "menuBar": { 15 | "name": "Barra de menu", 16 | "description": "Altera as configurações de visibilidade da barra nativa de menu.", 17 | "labels": { 18 | "hide": "Esconde a barra de menu automaticamente (tecla ALT altera a visibilidade)." 19 | } 20 | }, 21 | "tray": { 22 | "description": "Altera a visibilidade do ícone na área de notificação do sistema." 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sources/translations/pt/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "Não foi possível conectar ao serviço do Discord.", 4 | "description": "Verifique que você está conectado a internet." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "Sobre" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sources/translations/ru/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "tray": { 3 | "toggle": "Развернуть", 4 | "quit": "Закрыть", 5 | "mention": { 6 | "one": "Упоминание", 7 | "other": "упоминания" 8 | } 9 | }, 10 | "context": { 11 | "copy": "Копировать", 12 | "paste": "Вставить", 13 | "cut": "Вырезать", 14 | "dictionaryAdd": "Добавить в локальный словарь", 15 | "copyURL": "Копировать адрес ссылки", 16 | "copyURLText": "Копировать текст ссылки", 17 | "inspectElement": "Исследовать", 18 | "copyImage": "Копировать изображение", 19 | "copyImageURL": "Скопировать ссылку на изображение" 20 | }, 21 | "menubar": { 22 | "enabled": "Включено", 23 | "file": { 24 | "groupName": "Файл", 25 | "quit": "Закрыть", 26 | "relaunch": "Перезапустить", 27 | "addon": { 28 | "groupName": "Расширения", 29 | "loadNode": "Загрузить расширение Node", 30 | "loadChrome": "Загрузить расширение Chrome" 31 | } 32 | }, 33 | "edit": { 34 | "groupName": "Редактирование", 35 | "undo": "Отменить", 36 | "redo": "Повторить", 37 | "selectAll": "Выбрать все" 38 | }, 39 | "view": { 40 | "groupName": "Вид", 41 | "reload": "Обновить", 42 | "forceReload": "Принудительно обновить", 43 | "devTools": "Инструменты разработчика", 44 | "resetZoom": "Сбросить масштаб", 45 | "zoomIn": "Увеличить размер шрифта", 46 | "zoomOut": "Уменьшить размер шрифта", 47 | "fullScreen": "Полноэкранный режим" 48 | }, 49 | "window": { 50 | "groupName": "Окно", 51 | "mobileMode": "Скрыть боковую панель" 52 | } 53 | }, 54 | "windows": { 55 | "settings": "Настройки", 56 | "about": "О программе", 57 | "docs": "Документация", 58 | "invite": "Приглашение" 59 | }, 60 | "dialog": { 61 | "common": { 62 | "error": "Ошибка", 63 | "warning": "Предупреждение", 64 | "continue": "&Продолжить", 65 | "yes": "&Да", 66 | "no": "&Нет", 67 | "source": "Источник" 68 | }, 69 | "ver": { 70 | "updateBadge": "[ОБНОВЛЕНИЕ]", 71 | "updateTitle": "Доступно обновление!", 72 | "update": "Доступна новая версия приложения!", 73 | "recent": "Приложение является актуальной!", 74 | "devel": "Вы используете версию для разработчиков.", 75 | "downgrade": "Ваша версия новее стабильной!" 76 | }, 77 | "permission": { 78 | "request": { 79 | "denied": "%s: Запрос разрешения на элемент „%s” отклонён." 80 | }, 81 | "check": { 82 | "denied": "%s: Запрос проверки прав на элемент „%s” отклонён." 83 | }, 84 | "question": { 85 | "notifications": "уведомлениям", 86 | "title": "Обработка запросов на разрешение", 87 | "audio": "микрофону", 88 | "video": "веб-камере", 89 | "message": "Вы хотите предоставить Discord доступ к %s?" 90 | } 91 | }, 92 | "mod": { 93 | "nodeExt": "Расширение WebCord Node.js", 94 | "crx": "Расширение Chrome/Chromium" 95 | }, 96 | "externalApp": { 97 | "title": "Открытие ссылки во внешнем приложении", 98 | "message": "Веб-сайт пытается перенаправить ссылку с другим источником во внешнее приложение. Всё равно продолжить?" 99 | }, 100 | "screenShare": { 101 | "close": "Закрыть", 102 | "sound": { 103 | "unavailable": "Обмен аудио на этой платформе недоступен.", 104 | "system": "Обмен с устройством аудио." 105 | }, 106 | "source": { 107 | "entire": "Экран компьютера", 108 | "screen": "Экран" 109 | } 110 | } 111 | }, 112 | "help": { 113 | "groupName": "Помощь", 114 | "repo": "Репозиторий", 115 | "bugs": "Сообщить об ошибке" 116 | }, 117 | "log": { 118 | "singleInstance": "Переход на существующее окно...", 119 | "listenPort": "Прослушивание порта %s." 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sources/translations/ru/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "Невозможно подключиться к сервису Discord.", 4 | "description": "Убедитеть, что вы подключены к Интернету." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "О программе", 9 | "credits": "Благодарности", 10 | "licenses": "Лицензии" 11 | }, 12 | "about": { 13 | "appDescription": "Веб-клиент Discord и Fosscord с функциями, ориентированными на конфиденциальность.", 14 | "appRepo": "Репозиторий GitHub", 15 | "featuresTitle": "Флаги сборки", 16 | "featuresDefault": "По умолчанию", 17 | "checksumTitle": "Контрольная сумма" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Автор, управляющий и основной разработчик.", 22 | "contributors": { 23 | "default": "Соавтор кода." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "Лицензия приложения", 29 | "appLicenseBody": "%s является свободным программным обеспечением: вы можете использовать, изменять и распространять его на условиях лицензии MIT, которая должна распространяться вместе с этим программным обеспечением.", 30 | "showLicense": "Показать лицензию", 31 | "thirdPartyLicensesTitle": "Сторонние лицензии", 32 | "thirdPartyLicensesBody": "%s зависит от следующего стороннего программного обеспечения:", 33 | "licensedUnder": "под лицензией %s", 34 | "packageAuthors": "авторы %s" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/ta/client.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /sources/translations/ta/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "aboutWindow": { 3 | "nav": { 4 | "about": "பற்றி", 5 | "credits": "வரவு", 6 | "licenses": "உரிமங்கள்" 7 | }, 8 | "about": { 9 | "appDescription": "தனியுரிமையை மையமாகக் கொண்ட அம்சங்களுடன் முரண்பாடு மற்றும் ஃபோச்கார்ட் வலை வாங்கி.", 10 | "appRepo": "அறிவிலிமையம் களஞ்சியம்", 11 | "featuresTitle": "கொடிகளை உருவாக்குங்கள்", 12 | "featuresDefault": "இயல்புநிலை", 13 | "checksumTitle": "செக்சம்" 14 | }, 15 | "credits": { 16 | "people": { 17 | "author": "ஆசிரியர், பராமரிப்பாளர் மற்றும் முதன்மையான உருவாக்குபவர்.", 18 | "contributors": { 19 | "default": "குறியீடு பங்களிப்பாளர்." 20 | } 21 | } 22 | }, 23 | "licenses": { 24 | "appLicenseTitle": "விண்ணப்ப உரிமம்", 25 | "appLicenseBody": "%s ஒரு இலவச மென்பொருள்: நீங்கள் அதை எம்ஐடி உரிமத்தின் விதிமுறைகளின் கீழ் பயன்படுத்தலாம், மாற்றலாம் மற்றும் மறுபகிர்வு செய்யலாம், இது இந்த மென்பொருளுடன் விநியோகிக்கப்பட வேண்டும்.", 26 | "showLicense": "உரிமத்தைக் காட்டு", 27 | "thirdPartyLicensesTitle": "மூன்றாம் தரப்பு உரிமங்கள்", 28 | "thirdPartyLicensesBody": "%s மூன்றாம் தரப்பு மென்பொருளைப் பின்பற்றுவதைப் பொறுத்தது:", 29 | "licensedUnder": "%s உரிமத்தின் கீழ்", 30 | "packageAuthors": "%s ஆசிரியர்கள்" 31 | } 32 | }, 33 | "offline": { 34 | "title": "முரண்பாடு சேவையுடன் இணைக்க முடியாது.", 35 | "description": "நீங்கள் இணையத்துடன் இணைக்கப்பட்டுள்ளீர்கள் என்பதை உறுதிப்படுத்தவும்." 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/tr/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "tray": { 3 | "toggle": "Göster/gizle", 4 | "quit": "Çık", 5 | "mention": { 6 | "one": "bahset" 7 | } 8 | }, 9 | "context": { 10 | "copy": "Kopyala", 11 | "paste": "Yapıştır", 12 | "cut": "Kes", 13 | "dictionaryAdd": "Sözlüğe ekle", 14 | "copyURL": "Bağlantı adresini kopyala", 15 | "copyURLText": "Bağlantı metnini kopyala", 16 | "copyImage": "Resmi kopyala", 17 | "copyImageURL": "Resim bağlantısını kopyala", 18 | "inspectElement": "İncele" 19 | }, 20 | "menubar": { 21 | "enabled": "Etkin", 22 | "file": { 23 | "groupName": "Dosya", 24 | "quit": "Çık", 25 | "relaunch": "Yeniden başlat", 26 | "addon": { 27 | "groupName": "Eklentiler", 28 | "loadNode": "Node eklentisi yükle", 29 | "loadChrome": "Chrome eklentisi yükle" 30 | } 31 | }, 32 | "edit": { 33 | "groupName": "Düzenle", 34 | "undo": "Geri al", 35 | "redo": "Yeniden yap", 36 | "selectAll": "Hepsini seç" 37 | }, 38 | "view": { 39 | "groupName": "Görünüm", 40 | "reload": "Yeniden yükle", 41 | "forceReload": "Yeniden yüklemeyi zorla", 42 | "devTools": "Geliştirici araçları", 43 | "resetZoom": "Gerçek boyut", 44 | "zoomIn": "Yakınlaştır", 45 | "zoomOut": "Uzaklaştır", 46 | "fullScreen": "Tam ekrana geçiş yap" 47 | }, 48 | "window": { 49 | "groupName": "Pencere", 50 | "mobileMode": "Kenar çubuğunu gizle" 51 | } 52 | }, 53 | "windows": { 54 | "settings": "Ayarlar", 55 | "about": "Hakkında", 56 | "docs": "Dökümantasyon", 57 | "invite": "Davet" 58 | }, 59 | "dialog": { 60 | "common": { 61 | "error": "Hata", 62 | "warning": "Uyarı", 63 | "continue": "&Sürdür", 64 | "yes": "&Evet", 65 | "no": "&Hayır", 66 | "source": "Kaynak" 67 | }, 68 | "ver": { 69 | "updateBadge": "[GÜNCELLEME]", 70 | "updateTitle": "Güncelleme mevcut!", 71 | "update": "Yeni uygulama versiyonu mevcut!", 72 | "recent": "Uygulama güncel!", 73 | "devel": "Kararsız sürüm kullanıyorsunuz.", 74 | "downgrade": "Bu versiyon kararlı versiyondan daha yeni!" 75 | }, 76 | "permission": { 77 | "request": { 78 | "denied": "%s: %s için izin isteği reddedildi." 79 | }, 80 | "check": { 81 | "denied": "%s: %s için izin kontrolü reddedildi." 82 | }, 83 | "question": { 84 | "title": "İzin isteği işleyici", 85 | "message": "Discord'a %s erişim izni vermek istiyor musunuz?", 86 | "video": "kamera", 87 | "audio": "mikrofon", 88 | "notifications": "bildirimler" 89 | } 90 | }, 91 | "mod": { 92 | "nodeExt": "WebCord Node.js Eklentisi", 93 | "crx": "Chrome/Chromium Eklentisi" 94 | }, 95 | "externalApp": { 96 | "title": "Bağlantı başka uygulamada açılıyor", 97 | "message": "Bir web sitesi başka uygulamaya götüren kökeni farklı bir bağlantıya yönlendirmeye çalışıyor. Devam edilsin mi?" 98 | }, 99 | "screenShare": { 100 | "close": "Kapat", 101 | "sound": { 102 | "unavailable": "Ses paylaşımı bu platformda desteklenmiyor.", 103 | "system": "Cihaz sesini de paylaş." 104 | }, 105 | "source": { 106 | "entire": "Bilgisayar ekranı", 107 | "screen": "Ekran" 108 | } 109 | } 110 | }, 111 | "help": { 112 | "groupName": "Yardım", 113 | "repo": "Repository", 114 | "bugs": "Hata bildir" 115 | }, 116 | "log": { 117 | "singleInstance": "Mevcut pencereye geçiliyor...", 118 | "listenPort": "%s bağlantı noktasında dinleniyor." 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /sources/translations/tr/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "offline": { 3 | "title": "Discord'a bağlanılamıyor.", 4 | "description": "Lütfen internete bağlı olduğunuzdan emin olun." 5 | }, 6 | "aboutWindow": { 7 | "nav": { 8 | "about": "Detaylar", 9 | "credits": "Teşekkürler", 10 | "licenses": "Lisanslar" 11 | }, 12 | "about": { 13 | "appDescription": "Gizlilik odaklı özellikler içeren bir Discord ve Fosscord istemcisi.", 14 | "appRepo": "GitHub Repository", 15 | "featuresTitle": "Derleme parametreleri", 16 | "featuresDefault": "Varsayılan", 17 | "checksumTitle": "Checksum (doğrulama)" 18 | }, 19 | "credits": { 20 | "people": { 21 | "author": "Yapımcı, bakımcı ve ana geliştirici.", 22 | "contributors": { 23 | "default": "Yazılım destekçisi." 24 | } 25 | } 26 | }, 27 | "licenses": { 28 | "appLicenseTitle": "Uygulama lisansı", 29 | "appLicenseBody": "%s özgür bir yazılımdır: bu yazılımla birlikte dağıtılması gereken MIT lisansı koşulları kapsamında kullanabilir, değiştirebilir ve yeniden dağıtabilirsiniz.", 30 | "showLicense": "Lisansı göster", 31 | "thirdPartyLicensesTitle": "Üçüncü parti lisanslar", 32 | "thirdPartyLicensesBody": "%s şu üçüncü parti yazılımlara bağlıdır:", 33 | "licensedUnder": "%s lisansı altında", 34 | "packageAuthors": "%s yapımcıları" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/translations/uk/client.json: -------------------------------------------------------------------------------- 1 | { 2 | "menubar": { 3 | "file": { 4 | "quit": "Вийти", 5 | "relaunch": "Перезапустити", 6 | "groupName": "Файл", 7 | "addon": { 8 | "loadChrome": "Завантажити розширення для Chrome", 9 | "loadNode": "Завантажити Node.js розширення", 10 | "groupName": "Розширення" 11 | } 12 | }, 13 | "edit": { 14 | "selectAll": "Виділити все", 15 | "undo": "Скасувати", 16 | "redo": "Повернути", 17 | "groupName": "Редагувати" 18 | }, 19 | "view": { 20 | "zoomOut": "Зменшити масштаб", 21 | "groupName": "Перегляд", 22 | "reload": "Оновити", 23 | "resetZoom": "Скинути масштаб", 24 | "zoomIn": "Збільшити масштаб", 25 | "fullScreen": "Перемкнути повноекранний режим", 26 | "devTools": "Перемкнути інструменти розробника", 27 | "forceReload": "Примусово оновити" 28 | }, 29 | "window": { 30 | "groupName": "Вікно", 31 | "mobileMode": "Сховати бічну панель" 32 | }, 33 | "enabled": "Увімкнено" 34 | }, 35 | "context": { 36 | "paste": "Вставити", 37 | "copyImage": "Скопіювати зображення", 38 | "cut": "Вирізати", 39 | "copyURLText": "Скопіювати текст посилання", 40 | "copyURL": "Скопіювати адресу посилання", 41 | "copyImageURL": "Скопіювати посилання на зображення", 42 | "dictionaryAdd": "Додати до локального словника", 43 | "copy": "Скопіювати", 44 | "inspectElement": "Дослідити" 45 | }, 46 | "dialog": { 47 | "permission": { 48 | "question": { 49 | "notifications": "сповіщення", 50 | "message": "Ви бажаєте дати Discord дозвіл на %s?", 51 | "title": "Обробка запитів на дозвіл", 52 | "audio": "мікрофон", 53 | "video": "веб-камера" 54 | }, 55 | "request": { 56 | "denied": "%s: Запит на дозвіл для елемента \"%s\" відхилено." 57 | }, 58 | "check": { 59 | "denied": "%s: Запит на перевірку елемента \"%s\" відхилено." 60 | } 61 | }, 62 | "screenShare": { 63 | "close": "Закрити", 64 | "source": { 65 | "screen": "Екран", 66 | "entire": "Екран комп'ютера" 67 | }, 68 | "sound": { 69 | "unavailable": "Обмін аудіо не доступний на цій платформі.", 70 | "system": "Обмін з аудіо пристрою." 71 | } 72 | }, 73 | "ver": { 74 | "downgrade": "Ваша збірка для розробників новіша за стабільну!", 75 | "update": "Доступна нова версія додатку!", 76 | "recent": "Додаток оновлено до останньої версії!", 77 | "updateBadge": "[ОНОВЛЕННЯ]", 78 | "updateTitle": "Доступно оновлення!", 79 | "devel": "Ви використовуєте збірку для розробників." 80 | }, 81 | "externalApp": { 82 | "message": "Вебсайт намагається переправити посилання з іншим походженням у зовнішній додаток. Продовжити?", 83 | "title": "Відкриття посилання у зовнішньому додатку" 84 | }, 85 | "common": { 86 | "continue": "&Продовжити", 87 | "no": "&Ні", 88 | "yes": "&Так", 89 | "source": "Джерело", 90 | "warning": "Застереження", 91 | "error": "Помилка" 92 | }, 93 | "mod": { 94 | "nodeExt": "Розширення Node.js для WebCord", 95 | "crx": "Розширення для Chrome/Chromium" 96 | } 97 | }, 98 | "windows": { 99 | "settings": "Налаштування", 100 | "about": "Про додаток", 101 | "docs": "Документація", 102 | "invite": "Запрошення" 103 | }, 104 | "log": { 105 | "singleInstance": "Перемикання на існуюче вікно...", 106 | "listenPort": "Прослуховування порту %s." 107 | }, 108 | "help": { 109 | "groupName": "Допомога", 110 | "bugs": "Звітувати про помилку", 111 | "repo": "Репозиторій" 112 | }, 113 | "tray": { 114 | "mention": { 115 | "few": "І", 116 | "one": "згадка", 117 | "many": "згадок", 118 | "other": "згадки", 119 | "two": "І", 120 | "zero": "І" 121 | }, 122 | "toggle": "Перемкнути", 123 | "quit": "Вийти" 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /sources/translations/uk/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "aboutWindow": { 3 | "about": { 4 | "featuresTitle": "Флаги збірки", 5 | "featuresDefault": "За замовченням", 6 | "checksumTitle": "Контрольна сума", 7 | "appRepo": "GitHub репозиторій", 8 | "appDescription": "Веб клієнт Discord та Fosscord орієнтований на приватність." 9 | }, 10 | "licenses": { 11 | "appLicenseTitle": "Ліцензія додатку", 12 | "showLicense": "Показати ліцензію", 13 | "licensedUnder": "під ліцензією %s", 14 | "appLicenseBody": "%s це вільне програмне забезпечення: ви можете використовувати, модифікувати та розповсюджувати його під умовами MIT ліцензії, яка має розповсюджуватися з цим програмним забезпеченням.", 15 | "packageAuthors": "%s автори", 16 | "thirdPartyLicensesTitle": "Ліцензії третіх сторін", 17 | "thirdPartyLicensesBody": "%s залежить від наступного програмного забезпечення третіх сторін:" 18 | }, 19 | "credits": { 20 | "people": { 21 | "contributors": { 22 | "default": "Со-автори коду." 23 | }, 24 | "author": "Автор, супроводжувач і головний розробник." 25 | } 26 | }, 27 | "nav": { 28 | "about": "Про додаток", 29 | "credits": "Подяки", 30 | "licenses": "Ліцензії" 31 | } 32 | }, 33 | "offline": { 34 | "title": "Не вдається приєднатися до сервісу Discord.", 35 | "description": "Впевніться, що ви підключені до інтернету." 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "target": "ES2022", 7 | /* Whenever incremental compilation is enabled */ 8 | "incremental": true, 9 | /* File containing saved information about incremental compilation */ 10 | "tsBuildInfoFile": "cache/tsc.json", 11 | /* Whenever sourcemaps should be made for emitted JavaScript files */ 12 | "sourceMap": true, 13 | /* Directory in which are placed all emitted files */ 14 | "outDir": "app", 15 | /* Whenever comments are emitted to files */ 16 | "removeComments": true, 17 | /* Replace a set of core libraries. */ 18 | "lib": ["ES2022", "DOM", "DOM.Iterable"], 19 | /* Limit module resolution to project's path only */ 20 | "typeRoots": ["./node_modules/@types"], 21 | /* Use `tslib` for import helpers. */ 22 | "importHelpers": true, 23 | /* Allow for JSON imports */ 24 | "resolveJsonModule": true, 25 | /* Enforce consistent casing for imported module names */ 26 | "forceConsistentCasingInFileNames": true, 27 | /* Use *nix end-of-line character for emitted files */ 28 | "newLine": "lf", 29 | /* Do not emit anything when there's an error in source files */ 30 | "noEmitOnError": true, 31 | /* Do not enforce CJS syntax in TypeScript for imports/exports. */ 32 | "verbatimModuleSyntax": false, 33 | /* Do not ensure compatibility with other transpilers. */ 34 | "isolatedModules": false, 35 | /* Type workarounds for broken modules: */ 36 | "paths": { 37 | "marked-*": ["./node_modules/marked-*/src/index.d.ts"] 38 | } 39 | }, 40 | "include": ["sources/code/**/*.ts", "sources/code/**/*.mts",] 41 | } --------------------------------------------------------------------------------