Merge pull request #209 from schlagmichdoch/next
Release Next Version `v1.10.0`
2
.github/ISSUE_TEMPLATE/bug-report.md
vendored
|
@ -43,7 +43,7 @@ No | Yes
|
||||||
|
|
||||||
**Self-Hosted Setup**
|
**Self-Hosted Setup**
|
||||||
Proxy: Nginx | Apache2
|
Proxy: Nginx | Apache2
|
||||||
Deployment: docker run | docker-compose | npm run start:prod
|
Deployment: docker run | docker compose | npm run start:prod
|
||||||
Version: v1.9.4
|
Version: v1.9.4
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
|
|
13
.github/workflows/docker-image.yml
vendored
|
@ -1,3 +1,14 @@
|
||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
# GitHub recommends pinning actions to a commit SHA.
|
||||||
|
# To get a newer version, you will need to update the SHA.
|
||||||
|
# You can also reference a tag or branch, but the action may change without warning.
|
||||||
|
|
||||||
|
# Build a Docker image whenever it is pushed to master
|
||||||
|
|
||||||
name: Docker Image CI
|
name: Docker Image CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
@ -13,6 +24,6 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Build the Docker image
|
- name: Build the Docker image
|
||||||
run: docker build --pull . -f Dockerfile -t pairdrop
|
run: docker build --pull . -f Dockerfile -t pairdrop
|
||||||
|
|
14
.github/workflows/github-image.yml
vendored
|
@ -7,6 +7,8 @@
|
||||||
# To get a newer version, you will need to update the SHA.
|
# To get a newer version, you will need to update the SHA.
|
||||||
# You can also reference a tag or branch, but the action may change without warning.
|
# You can also reference a tag or branch, but the action may change without warning.
|
||||||
|
|
||||||
|
# Create a Docker image and push it to ghcr.io whenever a new version tag is pushed
|
||||||
|
|
||||||
name: GHCR Image CI
|
name: GHCR Image CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
@ -27,16 +29,16 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
|
||||||
- name: Setup qemu
|
- name: Setup qemu
|
||||||
uses: docker/setup-qemu-action@v2.1.0
|
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
|
||||||
|
|
||||||
- name: Setup Docker Buildx
|
- name: Setup Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2.5.0
|
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
@ -44,12 +46,12 @@ jobs:
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
uses: docker/metadata-action@31cebacef4805868f9ce9a0cb03ee36c32df2ac4 # v5.3.0
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|
35
.github/workflows/zip-release.yml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
# GitHub recommends pinning actions to a commit SHA.
|
||||||
|
# To get a newer version, you will need to update the SHA.
|
||||||
|
# You can also reference a tag or branch, but the action may change without warning.
|
||||||
|
|
||||||
|
# Create a new zip file from pairdrop-cli whenever a new version tag is pushed
|
||||||
|
|
||||||
|
name: Zip Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Archive Release
|
||||||
|
uses: thedoctor0/zip-release@b57d897cb5d60cb78b51a507f63fa184cfe35554 # v0.7.6
|
||||||
|
with:
|
||||||
|
type: 'zip'
|
||||||
|
filename: 'pairdrop-cli.zip'
|
||||||
|
path: 'pairdrop-cli'
|
||||||
|
exclusions: '*.git* /*node_modules/* .editorconfig'
|
||||||
|
- name: Upload Release
|
||||||
|
uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0
|
||||||
|
with:
|
||||||
|
artifacts: "pairdrop-cli.zip"
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
3
.gitignore
vendored
|
@ -3,3 +3,6 @@ node_modules
|
||||||
fqdn.env
|
fqdn.env
|
||||||
/docker/certs
|
/docker/certs
|
||||||
qrcode-svg/
|
qrcode-svg/
|
||||||
|
turnserver.conf
|
||||||
|
rtc_config.json
|
||||||
|
ssl/
|
||||||
|
|
|
@ -8,7 +8,12 @@ RUN npm ci
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# environment settings
|
||||||
|
ENV NODE_ENV="production"
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
CMD wget --quiet --tries=1 --spider http://localhost:3000 || exit 1
|
CMD wget --quiet --tries=1 --spider http://localhost:3000 || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["npm", "start"]
|
18
README.md
|
@ -67,10 +67,11 @@ Developed based on [Snapdrop](https://github.com/RobinLinus/snapdrop)
|
||||||
* Multiple files are transferred at once with an overall progress indicator
|
* Multiple files are transferred at once with an overall progress indicator
|
||||||
|
|
||||||
### Send Files or Text Directly From Share Menu, Context Menu or CLI
|
### Send Files or Text Directly From Share Menu, Context Menu or CLI
|
||||||
* [Send files directly from context menu on Windows](docs/how-to.md#send-files-directly-from-context-menu-on-windows)
|
* [Send files directly from context menu on Windows](docs/how-to.md#send-multiple-files-and-directories-directly-from-context-menu-on-windows)
|
||||||
* [Send directly from share menu on iOS](docs/how-to.md#send-directly-from-share-menu-on-ios)
|
* [Send files directly from context menu on Ubuntu (using Nautilus)](/docs/how-to.md#send-multiple-files-and-directories-directly-from-context-menu-on-ubuntu-using-nautilus)
|
||||||
* [Send directly from share menu on Android](docs/how-to.md#send-directly-from-share-menu-on-android)
|
* [Send files directly from share menu on iOS](docs/how-to.md#send-directly-from-share-menu-on-ios)
|
||||||
* [Send directly via command-line interface](docs/how-to.md#send-directly-via-command-line-interface)
|
* [Send files directly from share menu on Android](docs/how-to.md#send-directly-from-share-menu-on-android)
|
||||||
|
* [Send files directly via command-line interface](docs/how-to.md#send-directly-via-command-line-interface)
|
||||||
|
|
||||||
### Other changes
|
### Other changes
|
||||||
* Change your display name permanently to easily differentiate your devices
|
* Change your display name permanently to easily differentiate your devices
|
||||||
|
@ -85,7 +86,8 @@ Developed based on [Snapdrop](https://github.com/RobinLinus/snapdrop)
|
||||||
* Lots of stability fixes (Thanks [@MWY001](https://github.com/MWY001) [@skiby7](https://github.com/skiby7) and [@willstott101](https://github.com/willstott101))
|
* Lots of stability fixes (Thanks [@MWY001](https://github.com/MWY001) [@skiby7](https://github.com/skiby7) and [@willstott101](https://github.com/willstott101))
|
||||||
* To host PairDrop on your local network (e.g. on Raspberry Pi): [All peers connected with private IPs are discoverable by each other](https://github.com/RobinLinus/snapdrop/pull/558)
|
* To host PairDrop on your local network (e.g. on Raspberry Pi): [All peers connected with private IPs are discoverable by each other](https://github.com/RobinLinus/snapdrop/pull/558)
|
||||||
* When hosting PairDrop yourself you can [set your own STUN/TURN servers](docs/host-your-own.md#specify-stunturn-servers)
|
* When hosting PairDrop yourself you can [set your own STUN/TURN servers](docs/host-your-own.md#specify-stunturn-servers)
|
||||||
* Built-in translations
|
* Built-in translations via [Weblate](https://hosted.weblate.org/engage/pairdrop/)
|
||||||
|
* Airy design (Thanks [@Avieshek](https://linktr.ee/avieshek/))
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -98,9 +100,11 @@ Developed based on [Snapdrop](https://github.com/RobinLinus/snapdrop)
|
||||||
* [NodeJS](https://nodejs.org/en/) backend
|
* [NodeJS](https://nodejs.org/en/) backend
|
||||||
* [Progressive Web App](https://wikipedia.org/wiki/Progressive_Web_App)
|
* [Progressive Web App](https://wikipedia.org/wiki/Progressive_Web_App)
|
||||||
* [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
|
* [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
|
||||||
* [zip.js](https://gildas-lormeau.github.io/zip.js/)
|
|
||||||
* [cyrb53](https://github.com/bryc) super fast hash function
|
|
||||||
* [Weblate](https://weblate.org/) Web based localization tool
|
* [Weblate](https://weblate.org/) Web based localization tool
|
||||||
|
* [zip.js](https://github.com/gildas-lormeau/zip.js) JavaScript library to zip and unzip files ([BSD 3-Clause License](licenses/BSD_3-Clause-zip-js))
|
||||||
|
* [NoSleep](https://github.com/richtr/NoSleep.js) JavaScript library to prevent display sleep and enable wake lock in any Android or iOS web browser ([MIT License](licenses/MIT-NoSleep))
|
||||||
|
* [heic2any](https://github.com/alexcorvi/heic2any) JavaScript library to convert HEIC/HEIF images to PNG/GIF/JPEG ([MIT License](licenses/MIT-heic2any))
|
||||||
|
* [cyrb53](https://github.com/bryc) Super fast hash function
|
||||||
|
|
||||||
Have any questions? Read our [FAQ](docs/faq.md).
|
Have any questions? Read our [FAQ](docs/faq.md).
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,31 @@
|
||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
node:
|
pairdrop:
|
||||||
image: "node:lts-alpine"
|
image: "lscr.io/linuxserver/pairdrop:latest"
|
||||||
user: "node"
|
container_name: pairdrop
|
||||||
working_dir: /home/node/app
|
|
||||||
volumes:
|
|
||||||
- ./:/home/node/app
|
|
||||||
command: ash -c "npm i && npm run start:prod"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./rtc_config.json:/home/node/app/rtc_config.json
|
||||||
|
environment:
|
||||||
|
- PUID=1000 # UID to run the application as
|
||||||
|
- PGID=1000 # GID to run the application as
|
||||||
|
- WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
|
||||||
|
- RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
|
||||||
|
- RTC_CONFIG=/home/node/app/rtc_config.json # Set to the path of a file that specifies the STUN/TURN servers.
|
||||||
|
- DEBUG_MODE=false # Set to true to debug container and peer connections.
|
||||||
|
- TZ=Etc/UTC # Time Zone
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "127.0.0.1:3000:3000" # Web UI. Change the port number before the last colon e.g. `127.0.0.1:9000:3000`
|
||||||
coturn_server:
|
coturn_server:
|
||||||
image: "coturn/coturn"
|
image: "coturn/coturn"
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
network_mode: "host"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./turnserver.conf:/etc/coturn/turnserver.conf
|
- ./turnserver.conf:/etc/coturn/turnserver.conf
|
||||||
#you need to copy turnserver_example.conf to turnserver.conf and specify domain, IP address, user and password
|
- ./ssl/:/etc/coturn/ssl/
|
||||||
|
ports:
|
||||||
|
- "3478:3478"
|
||||||
|
- "3478:3478/udp"
|
||||||
|
- "5349:5349"
|
||||||
|
- "5349:5349/udp"
|
||||||
|
- "10000-20000:10000-20000/udp"
|
||||||
|
# see guide at docs/host-your-own.md#coturn-and-pairdrop-via-docker-compose
|
|
@ -1,12 +1,16 @@
|
||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
node:
|
pairdrop:
|
||||||
image: "node:lts-alpine"
|
image: "lscr.io/linuxserver/pairdrop:latest"
|
||||||
user: "node"
|
container_name: pairdrop
|
||||||
working_dir: /home/node/app
|
|
||||||
volumes:
|
|
||||||
- ./:/home/node/app
|
|
||||||
command: ash -c "npm i && npm run start:prod"
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000 # UID to run the application as
|
||||||
|
- PGID=1000 # GID to run the application as
|
||||||
|
- WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
|
||||||
|
- RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
|
||||||
|
- RTC_CONFIG=false # Set to the path of a file that specifies the STUN/TURN servers.
|
||||||
|
- DEBUG_MODE=false # Set to true to debug container and peer connections.
|
||||||
|
- TZ=Etc/UTC # Time Zone
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "127.0.0.1:3000:3000" # Web UI. Change the port number before the last colon e.g. `127.0.0.1:9000:3000`
|
||||||
|
|
|
@ -1,181 +1,132 @@
|
||||||
# Deployment Notes
|
# Deployment Notes
|
||||||
The easiest way to get PairDrop up and running is by using Docker.
|
|
||||||
|
|
||||||
> <b>TURN server for Internet Transfer</b>
|
## TURN server for Internet Transfer
|
||||||
>
|
|
||||||
> Beware that you have to host your own TURN server to enable transfers between different networks.
|
|
||||||
>
|
|
||||||
> Follow [this guide](https://gabrieltanner.org/blog/turn-server/) to either install coturn directly on your system (Step 1) \
|
|
||||||
> or deploy it via docker-compose (Step 5).
|
|
||||||
|
|
||||||
> <b>PairDrop via HTTPS</b>
|
Beware that you have to host your own TURN server to enable transfers between different networks.
|
||||||
>
|
|
||||||
> On some browsers PairDrop must be served over TLS in order for some feautures to work properly. These may include copying an incoming message via the 'copy' button, installing PairDrop as PWA, persistent pairing of devices and changing of the display name, and notifications. Naturally, this is also recommended to increase security.
|
Follow [this guide](https://gabrieltanner.org/blog/turn-server/) to either install coturn directly on your system (Step 1)
|
||||||
|
or deploy it via Docker (Step 5).
|
||||||
|
|
||||||
|
You can use the `docker-compose-coturn.yml` in this repository. See [Coturn and PairDrop via Docker Compose](#coturn-and-pairdrop-via-docker-compose).
|
||||||
|
|
||||||
|
Alternatively, use a free, pre-configured TURN server like [OpenRelay](https://www.metered.ca/tools/openrelay/)
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## PairDrop via HTTPS
|
||||||
|
|
||||||
|
On some browsers PairDrop must be served over TLS in order for some features to work properly.
|
||||||
|
These may include:
|
||||||
|
- Copying an incoming message via the 'copy' button
|
||||||
|
- Installing PairDrop as PWA
|
||||||
|
- Persistent pairing of devices
|
||||||
|
- Changing of the display name
|
||||||
|
- Notifications
|
||||||
|
|
||||||
|
Naturally, this is also recommended to increase security.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
## Deployment with Docker
|
## Deployment with Docker
|
||||||
|
|
||||||
|
The easiest way to get PairDrop up and running is by using Docker.
|
||||||
|
|
||||||
### Docker Image from Docker Hub
|
### Docker Image from Docker Hub
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 lscr.io/linuxserver/pairdrop
|
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 lscr.io/linuxserver/pairdrop
|
||||||
```
|
```
|
||||||
|
> This image is hosted by [linuxserver.io](https://linuxserver.io). For more information visit https://hub.docker.com/r/linuxserver/pairdrop
|
||||||
> You must use a server proxy to set the X-Forwarded-For \
|
|
||||||
> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
|
||||||
>
|
|
||||||
> To prevent bypassing the proxy by reaching the docker container directly, \
|
|
||||||
> `127.0.0.1` is specified in the run command.
|
|
||||||
|
|
||||||
#### Options / Flags
|
|
||||||
Set options by using the following flags in the `docker run` command:
|
|
||||||
|
|
||||||
##### Port
|
|
||||||
```bash
|
|
||||||
-p 127.0.0.1:8080:3000
|
|
||||||
```
|
|
||||||
> Specify the port used by the docker image
|
|
||||||
> - 3000 -> `-p 127.0.0.1:3000:3000`
|
|
||||||
> - 8080 -> `-p 127.0.0.1:8080:3000`
|
|
||||||
##### Rate limiting requests
|
|
||||||
```bash
|
|
||||||
-e RATE_LIMIT=true
|
|
||||||
```
|
|
||||||
> Limits clients to 1000 requests per 5 min
|
|
||||||
|
|
||||||
##### IPv6 Localization
|
|
||||||
```bash
|
|
||||||
-e IPV6_LOCALIZE=4
|
|
||||||
```
|
|
||||||
> To enable Peer Discovery among IPv6 peers, you can specify a reduced number of segments \
|
|
||||||
> of the client IPv6 address to be evaluated as the peer's IP. \
|
|
||||||
> This can be especially useful when using Cloudflare as a proxy.
|
|
||||||
>
|
|
||||||
> The flag must be set to an **integer** between `1` and `7`. \
|
|
||||||
> The number represents the number of IPv6 [hextets](https://en.wikipedia.org/wiki/IPv6#Address_representation) \
|
|
||||||
> to match the client IP against. The most common value would be `4`, \
|
|
||||||
> which will group peers within the same `/64` subnet.
|
|
||||||
|
|
||||||
##### Websocket Fallback (for VPN)
|
|
||||||
```bash
|
|
||||||
-e WS_FALLBACK=true
|
|
||||||
```
|
|
||||||
> Provides PairDrop to clients with an included websocket fallback \
|
|
||||||
> if the peer to peer WebRTC connection is not available to the client.
|
|
||||||
>
|
|
||||||
> This is not used on the official https://pairdrop.net website, \
|
|
||||||
> but you can activate it on your self-hosted instance.
|
|
||||||
> This is especially useful if you connect to your instance via a VPN (as most VPN services block WebRTC completely in order to hide your real IP address). ([Read more here](https://privacysavvy.com/security/safe-browsing/disable-webrtc-chrome-firefox-safari-opera-edge/)).
|
|
||||||
>
|
|
||||||
> **Warning:** All traffic sent between devices using this fallback \
|
|
||||||
> is routed through the server and therefor not peer to peer! \
|
|
||||||
> Beware that the traffic routed via this fallback is readable by the server. \
|
|
||||||
> Only ever use this on instances you can trust. \
|
|
||||||
> Additionally, beware that all traffic using this fallback debits the servers data plan.
|
|
||||||
|
|
||||||
##### Specify STUN/TURN Servers
|
|
||||||
```bash
|
|
||||||
-e RTC_CONFIG="rtc_config.json"
|
|
||||||
```
|
|
||||||
|
|
||||||
> Specify the STUN/TURN servers PairDrop clients use by setting \
|
|
||||||
> `RTC_CONFIG` to a JSON file including the configuration. \
|
|
||||||
> You can use `pairdrop/rtc_config_example.json` as a starting point.
|
|
||||||
>
|
|
||||||
> To host your own TURN server you can follow this guide: https://gabrieltanner.org/blog/turn-server/
|
|
||||||
> Alternatively, use a free, pre-configured TURN server like [OpenRelay]([url](https://www.metered.ca/tools/openrelay/))
|
|
||||||
>
|
|
||||||
> Default configuration:
|
|
||||||
> ```json
|
|
||||||
> {
|
|
||||||
> "sdpSemantics": "unified-plan",
|
|
||||||
> "iceServers": [
|
|
||||||
> {
|
|
||||||
> "urls": "stun:stun.l.google.com:19302"
|
|
||||||
> }
|
|
||||||
> ]
|
|
||||||
> }
|
|
||||||
> ```
|
|
||||||
|
|
||||||
##### Debug Mode
|
|
||||||
```bash
|
|
||||||
-e DEBUG_MODE="true"
|
|
||||||
```
|
|
||||||
|
|
||||||
> Use this flag to enable debugging information about the connecting peers IP addresses. \
|
|
||||||
> This is quite useful to check whether the [#HTTP-Server](#http-server) \
|
|
||||||
> is configured correctly, so the auto-discovery feature works correctly. \
|
|
||||||
> Otherwise, all clients discover each other mutually, independently of their network status.
|
|
||||||
>
|
|
||||||
> If this flag is set to `"true"` each peer that connects to the PairDrop server will produce a log to STDOUT like this:
|
|
||||||
> ```
|
|
||||||
> ----DEBUGGING-PEER-IP-START----
|
|
||||||
> remoteAddress: ::ffff:172.17.0.1
|
|
||||||
> x-forwarded-for: 19.117.63.126
|
|
||||||
> cf-connecting-ip: undefined
|
|
||||||
> PairDrop uses: 19.117.63.126
|
|
||||||
> IP is private: false
|
|
||||||
> if IP is private, '127.0.0.1' is used instead
|
|
||||||
> ----DEBUGGING-PEER-IP-END----
|
|
||||||
> ```
|
|
||||||
> If the IP PairDrop uses is the public IP of your device, everything is set up correctly. \
|
|
||||||
>To find out your devices public IP visit https://www.whatismyip.com/.
|
|
||||||
>
|
|
||||||
> To preserve your clients' privacy, **never use this flag in production!**
|
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
### Docker Image from GHCR
|
### Docker Image from GitHub Container Registry (ghcr.io)
|
||||||
```bash
|
|
||||||
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 ghcr.io/schlagmichdoch/pairdrop npm run start:prod
|
|
||||||
```
|
|
||||||
> You must use a server proxy to set the X-Forwarded-For to prevent \
|
|
||||||
> all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
|
||||||
>
|
|
||||||
> To prevent bypassing the proxy by reaching the Docker container directly, \
|
|
||||||
> `127.0.0.1` is specified in the run command.
|
|
||||||
>
|
|
||||||
> To specify options replace `npm run start:prod` \
|
|
||||||
> according to [the documentation below.](#options--flags-1)
|
|
||||||
|
|
||||||
> The Docker Image includes a healthcheck. \
|
```bash
|
||||||
> Read more about [Docker Swarm Usage](docker-swarm-usage.md#docker-swarm-usage).
|
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 ghcr.io/schlagmichdoch/pairdrop
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
### Docker Image self-built
|
### Docker Image self-built
|
||||||
|
|
||||||
#### Build the image
|
#### Build the image
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build --pull . -f Dockerfile -t pairdrop
|
docker build --pull . -f Dockerfile -t pairdrop
|
||||||
```
|
```
|
||||||
> A GitHub action is set up to do this step automatically.
|
|
||||||
|
> A GitHub action is set up to do this step automatically at the release of new versions.
|
||||||
>
|
>
|
||||||
> `--pull` ensures always the latest node image is used.
|
> `--pull` ensures always the latest node image is used.
|
||||||
|
|
||||||
#### Run the image
|
#### Run the image
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 -it pairdrop npm run start:prod
|
docker run -d --restart=unless-stopped --name=pairdrop -p 127.0.0.1:3000:3000 -it pairdrop
|
||||||
```
|
```
|
||||||
> You must use a server proxy to set the X-Forwarded-For \
|
|
||||||
|
> You must use a server proxy to set the `X-Forwarded-For` header
|
||||||
> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
>
|
>
|
||||||
> To prevent bypassing the proxy by reaching the Docker container \
|
> To prevent bypassing the proxy by reaching the docker container directly,
|
||||||
> directly, `127.0.0.1` is specified in the run command.
|
> `127.0.0.1` is specified in the run command.
|
||||||
>
|
|
||||||
> To specify options replace `npm run start:prod` \
|
|
||||||
> according to [the documentation below.](#options--flags-1)
|
|
||||||
|
|
||||||
> The Docker Image includes a Healthcheck. \
|
|
||||||
Read more about [Docker Swarm Usage](docker-swarm-usage.md#docker-swarm-usage).
|
<br>
|
||||||
|
|
||||||
|
### Flags
|
||||||
|
|
||||||
|
Set options by using the following flags in the `docker run` command:
|
||||||
|
|
||||||
|
#### Port
|
||||||
|
|
||||||
|
```bash
|
||||||
|
-p 127.0.0.1:8080:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
> Specify the port used by the docker image
|
||||||
|
>
|
||||||
|
> - 3000 -> `-p 127.0.0.1:3000:3000`
|
||||||
|
> - 8080 -> `-p 127.0.0.1:8080:3000`
|
||||||
|
|
||||||
|
#### Set Environment Variables via Docker
|
||||||
|
|
||||||
|
Environment Variables are set directly in the `docker run` command: \
|
||||||
|
e.g. `docker run -p 127.0.0.1:3000:3000 -it pairdrop -e DEBUG_MODE="true"`
|
||||||
|
|
||||||
|
Overview of available Environment Variables are found [here](#environment-variables).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name=pairdrop \
|
||||||
|
--restart=unless-stopped \
|
||||||
|
-p 127.0.0.1:3000:3000 \
|
||||||
|
-e PUID=1000 \
|
||||||
|
-e PGID=1000 \
|
||||||
|
-e WS_SERVER=false \
|
||||||
|
-e WS_FALLBACK=false \
|
||||||
|
-e RTC_CONFIG=false \
|
||||||
|
-e RATE_LIMIT=false \
|
||||||
|
-e DEBUG_MODE=false \
|
||||||
|
-e TZ=Etc/UTC \
|
||||||
|
lscr.io/linuxserver/pairdrop
|
||||||
|
```
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Deployment with Docker Compose
|
## Deployment with Docker Compose
|
||||||
Here's an example docker-compose file:
|
|
||||||
|
Here's an example docker compose file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "2"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
pairdrop:
|
pairdrop:
|
||||||
image: lscr.io/linuxserver/pairdrop:latest
|
image: "lscr.io/linuxserver/pairdrop:latest"
|
||||||
container_name: pairdrop
|
container_name: pairdrop
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
@ -183,22 +134,26 @@ services:
|
||||||
- PGID=1000 # GID to run the application as
|
- PGID=1000 # GID to run the application as
|
||||||
- WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
|
- WS_FALLBACK=false # Set to true to enable websocket fallback if the peer to peer WebRTC connection is not available to the client.
|
||||||
- RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
|
- RATE_LIMIT=false # Set to true to limit clients to 1000 requests per 5 min.
|
||||||
|
- RTC_CONFIG=false # Set to the path of a file that specifies the STUN/TURN servers.
|
||||||
|
- DEBUG_MODE=false # Set to true to debug container and peer connections.
|
||||||
- TZ=Etc/UTC # Time Zone
|
- TZ=Etc/UTC # Time Zone
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:3000:3000 # Web UI
|
- "127.0.0.1:3000:3000" # Web UI
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the compose file with `docker compose up -d`.
|
Run the compose file with `docker compose up -d`.
|
||||||
|
|
||||||
> You must use a server proxy to set the X-Forwarded-For \
|
> You must use a server proxy to set the `X-Forwarded-For` header
|
||||||
> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
>
|
>
|
||||||
> To prevent bypassing the proxy by reaching the Docker container \
|
> To prevent bypassing the proxy by reaching the Docker container
|
||||||
> directly, `127.0.0.1` is specified in the run command.
|
> directly, `127.0.0.1` is specified in the `ports` argument.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Deployment with node
|
## Deployment with Node.js
|
||||||
|
|
||||||
|
Clone this repository and enter the folder
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/schlagmichdoch/PairDrop.git && cd PairDrop
|
git clone https://github.com/schlagmichdoch/PairDrop.git && cd PairDrop
|
||||||
|
@ -212,56 +167,223 @@ npm install
|
||||||
|
|
||||||
Start the server with:
|
Start the server with:
|
||||||
|
|
||||||
```bash
|
|
||||||
node index.js
|
|
||||||
```
|
|
||||||
or
|
|
||||||
```bash
|
```bash
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
> Remember to check your IP address using your OS command to see where you can access the server.
|
|
||||||
|
|
||||||
> By default, the node server listens on port 3000.
|
> By default, the node server listens on port 3000.
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
### Environment variables
|
### Options / Flags
|
||||||
#### Port
|
|
||||||
On Unix based systems
|
These are some flags only reasonable when deploying via Node.js
|
||||||
```bash
|
|
||||||
PORT=3010 npm start
|
#### Port
|
||||||
```
|
|
||||||
On Windows
|
```bash
|
||||||
```bash
|
PORT=3000
|
||||||
$env:PORT=3010; npm start
|
```
|
||||||
```
|
|
||||||
> Specify the port PairDrop is running on. (Default: 3000)
|
> Default: `3000`
|
||||||
|
>
|
||||||
|
> Environment variable to specify the port used by the Node.js server \
|
||||||
|
> e.g. `PORT=3010 npm start`
|
||||||
|
|
||||||
|
#### Local Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start -- --localhost-only
|
||||||
|
```
|
||||||
|
|
||||||
|
> Only allow connections from localhost.
|
||||||
|
>
|
||||||
|
> You must use a server proxy to set the `X-Forwarded-For` header
|
||||||
|
> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
||||||
|
>
|
||||||
|
> Use this when deploying PairDrop with node to prevent
|
||||||
|
> bypassing the reverse proxy by reaching the Node.js server directly.
|
||||||
|
|
||||||
|
#### Automatic restart on error
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start -- --auto-restart
|
||||||
|
```
|
||||||
|
|
||||||
|
> Restarts server automatically on error
|
||||||
|
|
||||||
|
#### Production (autostart and rate-limit)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run start:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
> shortcut for `RATE_LIMIT=5 npm start -- --auto-restart`
|
||||||
|
|
||||||
|
#### Production (autostart, rate-limit, localhost-only)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run start:prod -- --localhost-only
|
||||||
|
```
|
||||||
|
|
||||||
|
> To prevent connections to the node server from bypassing \
|
||||||
|
> the proxy server you should always use "--localhost-only" on production.
|
||||||
|
|
||||||
|
#### Set Environment Variables via Node.js
|
||||||
|
|
||||||
|
To specify environment variables set them in the run command in front of `npm start`.
|
||||||
|
The syntax is different on Unix and Windows.
|
||||||
|
|
||||||
|
On Unix based systems
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PORT=3000 RTC_CONFIG="rtc_config.json" npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$env:PORT=3000 RTC_CONFIG="rtc_config.json"; npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
Overview of available Environment Variables are found [here](#environment-variables).
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DEBUG_MODE="true"
|
||||||
|
```
|
||||||
|
|
||||||
|
> Default: `false`
|
||||||
|
>
|
||||||
|
> Logs the used environment variables for debugging.
|
||||||
|
>
|
||||||
|
> Prints debugging information about the connecting peers IP addresses.
|
||||||
|
>
|
||||||
|
> This is quite useful to check whether the [#HTTP-Server](#http-server)
|
||||||
|
> is configured correctly, so the auto-discovery feature works correctly.
|
||||||
|
> Otherwise, all clients discover each other mutually, independently of their network status.
|
||||||
|
>
|
||||||
|
> If this flag is set to `"true"` each peer that connects to the PairDrop server will produce a log to STDOUT like this:
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
> ----DEBUGGING-PEER-IP-START----
|
||||||
|
> remoteAddress: ::ffff:172.17.0.1
|
||||||
|
> x-forwarded-for: 19.117.63.126
|
||||||
|
> cf-connecting-ip: undefined
|
||||||
|
> PairDrop uses: 19.117.63.126
|
||||||
|
> IP is private: false
|
||||||
|
> if IP is private, '127.0.0.1' is used instead
|
||||||
|
> ----DEBUGGING-PEER-IP-END----
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> If the IP address "PairDrop uses" matches the public IP address of the client device, everything is set up correctly. \
|
||||||
|
> To find out the public IP address of the client device visit https://whatsmyip.com/.
|
||||||
|
>
|
||||||
|
> To preserve your clients' privacy: \
|
||||||
|
> **Never use this environment variable in production!**
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Rate limiting requests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RATE_LIMIT=1
|
||||||
|
```
|
||||||
|
|
||||||
|
> Default: `false`
|
||||||
|
>
|
||||||
|
> Limits clients to 1000 requests per 5 min
|
||||||
|
>
|
||||||
|
> "If you are behind a proxy/load balancer (usually the case with most hosting services, e.g. Heroku, Bluemix, AWS ELB,
|
||||||
|
> Render, Nginx, Cloudflare, Akamai, Fastly, Firebase Hosting, Rackspace LB, Riverbed Stingray, etc.), the IP address of
|
||||||
|
> the request might be the IP of the load balancer/reverse proxy (making the rate limiter effectively a global one and
|
||||||
|
> blocking all requests once the limit is reached) or undefined."
|
||||||
|
> (See: https://express-rate-limit.mintlify.app/guides/troubleshooting-proxy-issues)
|
||||||
|
>
|
||||||
|
> To find the correct number to use for this setting:
|
||||||
|
>
|
||||||
|
> 1. Start PairDrop with `DEBUG_MODE=True` and `RATE_LIMIT=1`
|
||||||
|
> 2. Make a `get` request to `/ip` of the PairDrop instance (e.g. `https://pairdrop-example.net/ip`)
|
||||||
|
> 3. Check if the IP address returned in the response matches your public IP address (find out by visiting e.g. https://whatsmyip.com/)
|
||||||
|
> 4. You have found the correct number if the IP addresses match. If not, then increase `RATE_LIMIT` by one and redo 1. - 4.
|
||||||
|
>
|
||||||
|
> e.g. on Render you must use RATE_LIMIT=5
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### IPv6 Localization
|
||||||
|
|
||||||
#### IPv6 Localization
|
|
||||||
```bash
|
```bash
|
||||||
IPV6_LOCALIZE=4
|
IPV6_LOCALIZE=4
|
||||||
```
|
```
|
||||||
> Truncate a portion of the client IPv6 address to make peers more discoverable. \
|
|
||||||
> See [Options/Flags](#options--flags) above.
|
|
||||||
|
|
||||||
#### Specify STUN/TURN Server
|
> Default: `false`
|
||||||
On Unix based systems
|
>
|
||||||
|
> To enable Peer Auto-Discovery among IPv6 peers, you can specify a reduced number of segments \
|
||||||
|
> of the client IPv6 address to be evaluated as the peer's IP. \
|
||||||
|
> This can be especially useful when using Cloudflare as a proxy.
|
||||||
|
>
|
||||||
|
> The flag must be set to an **integer** between `1` and `7`. \
|
||||||
|
> The number represents the number of IPv6 [hextets](https://en.wikipedia.org/wiki/IPv6#Address_representation) \
|
||||||
|
> to match the client IP against. The most common value would be `4`, \
|
||||||
|
> which will group peers within the same `/64` subnet.
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Websocket Fallback (for VPN)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
RTC_CONFIG="rtc_config.json" npm start
|
WS_FALLBACK=true
|
||||||
```
|
```
|
||||||
On Windows
|
|
||||||
```bash
|
> Default: `false`
|
||||||
$env:RTC_CONFIG="rtc_config.json"; npm start
|
>
|
||||||
```
|
> Provides PairDrop to clients with an included websocket fallback \
|
||||||
> Specify the STUN/TURN servers PairDrop clients use by setting `RTC_CONFIG` \
|
> if the peer to peer WebRTC connection is not available to the client.
|
||||||
> to a JSON file including the configuration. \
|
>
|
||||||
> You can use `pairdrop/rtc_config_example.json` as a starting point.
|
> This is not used on the official https://pairdrop.net website,
|
||||||
|
> but you can activate it on your self-hosted instance.\
|
||||||
|
> This is especially useful if you connect to your instance via a VPN (as most VPN services block WebRTC completely in
|
||||||
|
> order to hide your real IP address). ([Read more here](https://privacysavvy.com/security/safe-browsing/disable-webrtc-chrome-firefox-safari-opera-edge/)).
|
||||||
|
>
|
||||||
|
> **Warning:** \
|
||||||
|
> All traffic sent between devices using this fallback
|
||||||
|
> is routed through the server and therefor not peer to peer!
|
||||||
>
|
>
|
||||||
> To host your own TURN server you can follow this guide: \
|
> Beware that the traffic routed via this fallback is readable by the server. \
|
||||||
> https://gabrieltanner.org/blog/turn-server/
|
> Only ever use this on instances you can trust.
|
||||||
|
>
|
||||||
|
> Additionally, beware that all traffic using this fallback debits the servers data plan.
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Specify STUN/TURN Servers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RTC_CONFIG="rtc_config.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
> Default: `false`
|
||||||
|
>
|
||||||
|
> Specify the STUN/TURN servers PairDrop clients use by setting \
|
||||||
|
> `RTC_CONFIG` to a JSON file including the configuration. \
|
||||||
|
> You can use `rtc_config_example.json` as a starting point.
|
||||||
|
>
|
||||||
|
> To host your own TURN server you can follow this guide: https://gabrieltanner.org/blog/turn-server/
|
||||||
|
> Alternatively, use a free, pre-configured TURN server like [OpenRelay](<[url](https://www.metered.ca/tools/openrelay/)>)
|
||||||
>
|
>
|
||||||
> Default configuration:
|
> Default configuration:
|
||||||
|
>
|
||||||
> ```json
|
> ```json
|
||||||
> {
|
> {
|
||||||
> "sdpSemantics": "unified-plan",
|
> "sdpSemantics": "unified-plan",
|
||||||
|
@ -273,109 +395,51 @@ $env:RTC_CONFIG="rtc_config.json"; npm start
|
||||||
> }
|
> }
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
#### Debug Mode
|
<br>
|
||||||
On Unix based systems
|
|
||||||
|
You can host an instance that uses another signaling server
|
||||||
|
This can be useful if you don't want to trust the client files that are hosted on another instance but still want to connect to devices that use https://pairdrop.net.
|
||||||
|
### Host Websocket Server (for VPN)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
DEBUG_MODE="true" npm start
|
SIGNALING_SERVER="pairdrop.net"
|
||||||
```
|
|
||||||
On Windows
|
|
||||||
```bash
|
|
||||||
$env:DEBUG_MODE="true"; npm start
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> Use this flag to enable debugging info about the connecting peers IP addresses. \
|
> Default: `false`
|
||||||
> This is quite useful to check whether the [#HTTP-Server](#http-server) \
|
|
||||||
> is configured correctly, so the auto discovery feature works correctly. \
|
|
||||||
> Otherwise, all clients discover each other mutually, independently of their network status.
|
|
||||||
>
|
>
|
||||||
> If this flag is set to `"true"` each peer that connects to the \
|
> By default, clients connecting to your instance use the signaling server of your instance to connect to other devices.
|
||||||
> PairDrop server will produce a log to STDOUT like this:
|
|
||||||
> ```
|
|
||||||
> ----DEBUGGING-PEER-IP-START----
|
|
||||||
> remoteAddress: ::ffff:172.17.0.1
|
|
||||||
> x-forwarded-for: 19.117.63.126
|
|
||||||
> cf-connecting-ip: undefined
|
|
||||||
> PairDrop uses: 19.117.63.126
|
|
||||||
> IP is private: false
|
|
||||||
> if IP is private, '127.0.0.1' is used instead
|
|
||||||
> ----DEBUGGING-PEER-IP-END----
|
|
||||||
> ```
|
|
||||||
> If the IP PairDrop uses is the public IP of your device everything is set up correctly. \
|
|
||||||
>Find your devices public IP by visiting https://www.whatismyip.com/.
|
|
||||||
>
|
|
||||||
> Preserve your clients' privacy. **Never use this flag in production!**
|
|
||||||
|
|
||||||
|
|
||||||
### Options / Flags
|
|
||||||
#### Local Run
|
|
||||||
```bash
|
|
||||||
npm start -- --localhost-only
|
|
||||||
```
|
|
||||||
> Only allow connections from localhost.
|
|
||||||
>
|
>
|
||||||
> You must use a server proxy to set the X-Forwarded-For \
|
> By using `SIGNALING_SERVER`, you can host an instance that uses another signaling server.
|
||||||
> to prevent all clients from discovering each other (See [#HTTP-Server](#http-server)).
|
|
||||||
>
|
|
||||||
> Use this when deploying PairDrop with node to prevent \
|
|
||||||
> bypassing the proxy by reaching the Docker container directly.
|
|
||||||
|
|
||||||
#### Automatic restart on error
|
|
||||||
```bash
|
|
||||||
npm start -- --auto-restart
|
|
||||||
```
|
|
||||||
> Restarts server automatically on error
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
#### Rate limiting requests
|
|
||||||
```bash
|
|
||||||
npm start -- --rate-limit
|
|
||||||
```
|
|
||||||
> Limits clients to 1000 requests per 5 min
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
#### Websocket Fallback (for VPN)
|
|
||||||
```bash
|
|
||||||
npm start -- --include-ws-fallback
|
|
||||||
```
|
|
||||||
> Provides PairDrop to clients with an included websocket fallback \
|
|
||||||
> if the peer to peer WebRTC connection is not available to the client.
|
|
||||||
>
|
|
||||||
> This is not used on the official https://pairdrop.net, \
|
|
||||||
but you can activate it on your self-hosted instance. \
|
|
||||||
> This is especially useful if you connect to your instance \
|
|
||||||
> via a VPN as most VPN services block WebRTC completely in order to hide your real IP address.
|
|
||||||
> ([Read more](https://privacysavvy.com/security/safe-browsing/disable-webrtc-chrome-firefox-safari-opera-edge/)).
|
|
||||||
>
|
>
|
||||||
> **Warning:** All traffic sent between devices using this fallback \
|
> This can be useful if you want to ensure the integrity of the client files and don't want to trust the client files that are hosted on another PairDrop instance but still want to connect to devices that use the other instance.
|
||||||
> is routed through the server and therefor not peer to peer! \
|
> E.g. host your own client files under *pairdrop.your-domain.com* but use the official signaling server under *pairdrop.net*
|
||||||
> Beware that the traffic routed via this fallback is readable by the server. \
|
> This way devices connecting to *pairdrop.your-domain.com* and *pairdrop.net* can discover each other.
|
||||||
> Only ever use this on instances you can trust. \
|
>
|
||||||
> Additionally, beware that all traffic using this fallback debits the servers data plan.
|
> Beware that the version of your PairDrop server is compatible with the version of the signaling server.
|
||||||
|
>
|
||||||
|
> `WS_SERVER` must be a valid url without the protocol prefix.
|
||||||
|
> Examples of valid values: `pairdrop.net`, `pairdrop.your-domain.com:3000`, `your-domain.com/pairdrop`
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
#### Production (autostart and rate-limit)
|
## Healthcheck
|
||||||
```bash
|
|
||||||
npm run start:prod
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Production (autostart, rate-limit, localhost-only and websocket fallback for VPN)
|
> The Docker Image hosted on `ghcr.io` and the self-built Docker Image include a healthcheck.
|
||||||
```bash
|
>
|
||||||
npm run start:prod -- --localhost-only --include-ws-fallback
|
> Read more about [Docker Swarm Usage](docker-swarm-usage.md#docker-swarm-usage).
|
||||||
```
|
|
||||||
> To prevent connections to the node server from bypassing \
|
<br>
|
||||||
> the proxy server you should always use "--localhost-only" on production.
|
|
||||||
|
|
||||||
## HTTP-Server
|
## HTTP-Server
|
||||||
|
|
||||||
When running PairDrop, the `X-Forwarded-For` header has to be set by a proxy. \
|
When running PairDrop, the `X-Forwarded-For` header has to be set by a proxy. \
|
||||||
Otherwise, all clients will be mutually visible.
|
Otherwise, all clients will be mutually visible.
|
||||||
|
|
||||||
To check if your setup is configured correctly [use the environment variable `DEBUG_MODE="true"`](#debug-mode).
|
To check if your setup is configured correctly [use the environment variable `DEBUG_MODE="true"`](#debug-mode).
|
||||||
|
|
||||||
### Using nginx
|
### Using nginx
|
||||||
|
|
||||||
#### Allow http and https requests
|
#### Allow http and https requests
|
||||||
|
|
||||||
```
|
```
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
@ -409,6 +473,7 @@ server {
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Automatic http to https redirect:
|
#### Automatic http to https redirect:
|
||||||
|
|
||||||
```
|
```
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
@ -437,14 +502,21 @@ server {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
### Using Apache
|
### Using Apache
|
||||||
|
|
||||||
install modules `proxy`, `proxy_http`, `mod_proxy_wstunnel`
|
install modules `proxy`, `proxy_http`, `mod_proxy_wstunnel`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
a2enmod proxy
|
a2enmod proxy
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
a2enmod proxy_http
|
a2enmod proxy_http
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
a2enmod proxy_wstunnel
|
a2enmod proxy_wstunnel
|
||||||
```
|
```
|
||||||
|
@ -454,16 +526,18 @@ a2enmod proxy_wstunnel
|
||||||
Create a new configuration file under `/etc/apache2/sites-available` (on Debian)
|
Create a new configuration file under `/etc/apache2/sites-available` (on Debian)
|
||||||
|
|
||||||
**pairdrop.conf**
|
**pairdrop.conf**
|
||||||
|
|
||||||
#### Allow HTTP and HTTPS requests
|
#### Allow HTTP and HTTPS requests
|
||||||
|
|
||||||
```apacheconf
|
```apacheconf
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
ProxyPass / http://127.0.0.1:3000/
|
ProxyPass / http://127.0.0.1:3000/
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
||||||
RewriteCond %{HTTP:Connection} upgrade [NC]
|
RewriteCond %{HTTP:Connection} upgrade [NC]
|
||||||
RewriteRule ^/?(.*) "ws://127.0.0.1:3000/$1" [P,L]
|
RewriteRule ^/?(.*) "ws://127.0.0.1:3000/$1" [P,L]
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
<VirtualHost *:443>
|
<VirtualHost *:443>
|
||||||
ProxyPass / https://127.0.0.1:3000/
|
ProxyPass / https://127.0.0.1:3000/
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
||||||
|
@ -471,12 +545,14 @@ Create a new configuration file under `/etc/apache2/sites-available` (on Debian)
|
||||||
RewriteRule ^/?(.*) "wws://127.0.0.1:3000/$1" [P,L]
|
RewriteRule ^/?(.*) "wws://127.0.0.1:3000/$1" [P,L]
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Automatic HTTP to HTTPS redirect:
|
#### Automatic HTTP to HTTPS redirect:
|
||||||
|
|
||||||
```apacheconf
|
```apacheconf
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
Redirect permanent / https://127.0.0.1:3000/
|
Redirect permanent / https://127.0.0.1:3000/
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
<VirtualHost *:443>
|
<VirtualHost *:443>
|
||||||
ProxyPass / https://127.0.0.1:3000/
|
ProxyPass / https://127.0.0.1:3000/
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
||||||
|
@ -484,62 +560,120 @@ Create a new configuration file under `/etc/apache2/sites-available` (on Debian)
|
||||||
RewriteRule ^/?(.*) "wws://127.0.0.1:3000/$1" [P,L]
|
RewriteRule ^/?(.*) "wws://127.0.0.1:3000/$1" [P,L]
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
```
|
```
|
||||||
|
|
||||||
Activate the new virtual host and reload Apache:
|
Activate the new virtual host and reload Apache:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
a2ensite pairdrop
|
a2ensite pairdrop
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
service apache2 reload
|
service apache2 reload
|
||||||
```
|
```
|
||||||
|
|
||||||
# Local Development
|
<br>
|
||||||
## Install
|
|
||||||
|
## Coturn and PairDrop via Docker Compose
|
||||||
|
|
||||||
|
### Setup container
|
||||||
|
To run coturn and PairDrop at once by using the `docker-compose-coturn.yml` with TURN over TLS enabled
|
||||||
|
you need to follow these steps:
|
||||||
|
|
||||||
|
1. Generate or retrieve certificates for your `<DOMAIN>` (e.g. letsencrypt / certbot)
|
||||||
|
2. Create `./ssl` folder: `mkdir ssl`
|
||||||
|
3. Copy your ssl-certificates and the privkey to `./ssl`
|
||||||
|
4. Restrict access to `./ssl`: `chown -R nobody:nogroup ./ssl`
|
||||||
|
5. Create a dh-params file: `openssl dhparam -out ./ssl/dhparams.pem 4096`
|
||||||
|
6. Copy `rtc_config_example.json` to `rtc_config.json`
|
||||||
|
7. Copy `turnserver_example.conf` to `turnserver.conf`
|
||||||
|
8. Change `<DOMAIN>` in both files to the domain where your PairDrop instance is running
|
||||||
|
9. Change `username` and `password` in `turnserver.conf` and `rtc-config.json`
|
||||||
|
10. To start the container including coturn run: \
|
||||||
|
`docker compose -f docker-compose-coturn.yml up -d`
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### Setup container
|
||||||
|
To restart the container including coturn run: \
|
||||||
|
`docker compose -f docker-compose-coturn.yml restart`
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### Setup container
|
||||||
|
To stop the container including coturn run: \
|
||||||
|
`docker compose -f docker-compose-coturn.yml stop`
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Firewall
|
||||||
|
To run PairDrop including its own coturn-server you need to punch holes in the firewall. These ports must be opened additionally:
|
||||||
|
- 3478 tcp/udp
|
||||||
|
- 5349 tcp/udp
|
||||||
|
- 10000:20000 tcp/udp
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Firewall
|
||||||
|
To run PairDrop including its own coturn-server you need to punch holes in the firewall. These ports must be opened additionally:
|
||||||
|
- 3478 tcp/udp
|
||||||
|
- 5349 tcp/udp
|
||||||
|
- 10000:20000 tcp/udp
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
All files needed for developing are available on the branch `dev`.
|
All files needed for developing are available on the branch `dev`.
|
||||||
|
|
||||||
First, [Install docker with docker-compose.](https://docs.docker.com/compose/install/)
|
First, [Install docker with docker-compose.](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
Then, clone the repository and run docker-compose:
|
Then, clone the repository and run docker-compose:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/schlagmichdoch/PairDrop.git
|
git clone https://github.com/schlagmichdoch/PairDrop.git && cd PairDrop
|
||||||
|
|
||||||
cd PairDrop
|
|
||||||
|
|
||||||
git checkout dev
|
|
||||||
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
```
|
||||||
|
```bash
|
||||||
|
git checkout dev
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose-dev.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
Now point your web browser to `http://localhost:8080`.
|
Now point your web browser to `http://localhost:8080`.
|
||||||
|
|
||||||
- To restart the containers, run `docker-compose restart`.
|
- To restart the containers, run `docker compose restart`.
|
||||||
- To stop the containers, run `docker-compose stop`.
|
- To stop the containers, run `docker compose stop`.
|
||||||
- To debug the NodeJS server, run `docker logs pairdrop_node_1`.
|
- To debug the Node.js server, run `docker logs pairdrop`.
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Testing PWA related features
|
### Testing PWA related features
|
||||||
|
|
||||||
PWAs requires the app to be served under a correctly set up and trusted TLS endpoint.
|
PWAs requires the app to be served under a correctly set up and trusted TLS endpoint.
|
||||||
|
|
||||||
The NGINX container creates a CA certificate and a website certificate for you. \
|
The NGINX container creates a CA certificate and a website certificate for you.
|
||||||
To correctly set the common name of the certificate, \
|
To correctly set the common name of the certificate,
|
||||||
you need to change the FQDN environment variable in `docker/fqdn.env` \
|
you need to change the FQDN environment variable in `docker/fqdn.env`
|
||||||
to the fully qualified domain name of your workstation.
|
to the fully qualified domain name of your workstation.
|
||||||
|
|
||||||
If you want to test PWA features, you need to trust the CA of the certificate for your local deployment. \
|
If you want to test PWA features, you need to trust the CA of the certificate for your local deployment. \
|
||||||
For your convenience, you can download the crt file from `http://<Your FQDN>:8080/ca.crt`. \
|
For your convenience, you can download the crt file from `http://<Your FQDN>:8080/ca.crt`. \
|
||||||
Install that certificate to the trust store of your operating system. \
|
Install that certificate to the trust store of your operating system. \
|
||||||
- On Windows, make sure to install it to the `Trusted Root Certification Authorities` store. \
|
|
||||||
- On macOS, double-click the installed CA certificate in `Keychain Access`, \
|
- On Windows, make sure to install it to the `Trusted Root Certification Authorities` store.
|
||||||
- expand `Trust`, and select `Always Trust` for SSL. \
|
- On macOS, double-click the installed CA certificate in `Keychain Access`,
|
||||||
- Firefox uses its own trust store. To install the CA, \
|
- expand `Trust`, and select `Always Trust` for SSL.
|
||||||
- point Firefox at `http://<Your FQDN>:8080/ca.crt`. \
|
- Firefox uses its own trust store. To install the CA,
|
||||||
- When prompted, select `Trust this CA to identify websites` and click *OK*. \
|
- point Firefox at `http://<Your FQDN>:8080/ca.crt`.
|
||||||
- When using Chrome, you need to restart Chrome so it reloads the trust store (`chrome://restart`). \
|
- When prompted, select `Trust this CA to identify websites` and click _OK_.
|
||||||
- Additionally, after installing a new cert, \
|
- When using Chrome, you need to restart Chrome so it reloads the trust store (`chrome://restart`).
|
||||||
- you need to clear the Storage (DevTools → Application → Clear storage → Clear site data).
|
- Additionally, after installing a new cert, you need to clear the Storage (DevTools → Application → Clear storage → Clear site data).
|
||||||
|
|
||||||
Please note that the certificates (CA and webserver cert) expire after a day.
|
Please note that the certificates (CA and webserver cert) expire after a day.
|
||||||
Also, whenever you restart the NGINX Docker, container new certificates are created.
|
Also, whenever you restart the NGINX Docker container new certificates are created.
|
||||||
|
|
||||||
The site is served on `https://<Your FQDN>:8443`.
|
The site is served on `https://<Your FQDN>:8443`.
|
||||||
|
|
||||||
|
|
132
docs/how-to.md
|
@ -1,84 +1,120 @@
|
||||||
# How-To
|
# How-To
|
||||||
## Send files directly from context menu on Windows
|
|
||||||
### Registering to open files with PairDrop
|
|
||||||
The [File Handling API](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files) is implemented
|
|
||||||
|
|
||||||
This is still experimental and must be enabled via a flag **before** the PWA is installed to Windows.
|
|
||||||
1. [Enabled feature in Edge](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files#enable-the-file-handling-api)
|
|
||||||
2. Install PairDrop by visiting https://pairdrop.net/ with the Edge web browser and install it as described [here](faq.md#help--i-cant-install-the-pwa-).
|
|
||||||
3. You are done! You can now send most files one at a time via PairDrop:
|
|
||||||
|
|
||||||
_context menu > Open with > PairDrop_
|
|
||||||
|
|
||||||
[//]: # (Todo: add screenshots)
|
|
||||||
|
|
||||||
### Sending multiple files to PairDrop
|
|
||||||
Outstandingly, it is also possible to send multiple files to PairDrop \
|
|
||||||
via the context menu by adding PairDrop to the `Send to` menu:
|
|
||||||
1. [Register PairDrop as file handler](#registering-to-open-files-with-pairdrop)
|
|
||||||
2. Hit Windows Key+R, type: `shell:programs` and hit Enter.
|
|
||||||
3. Copy the PairDrop shortcut from the directory
|
|
||||||
4. Hit Windows Key+R, type: `shell:sendto` and hit Enter.
|
|
||||||
5. Paste the copied shortcut into the directory
|
|
||||||
6. You are done! You can now send multiple files (but no directories) directly via PairDrop:
|
|
||||||
|
|
||||||
_context menu > Send to > PairDrop_
|
|
||||||
|
|
||||||
[//]: # (Todo: add screenshots)
|
|
||||||
|
|
||||||
## Send directly from share menu on iOS
|
## Send directly from share menu on iOS
|
||||||
I created an iOS shortcut to send images, files, folder, URLs \
|
I created an iOS shortcut to send images, files, folder, URLs \
|
||||||
or text directly from the share-menu
|
or text directly from the share-menu
|
||||||
https://routinehub.co/shortcut/13990/
|
https://routinehub.co/shortcut/13990/
|
||||||
|
|
||||||
[//]: # (Todo: add doku with screenshots)
|
[//]: # (Todo: Add screenshots)
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
## Send directly from share menu on Android
|
## Send directly from share menu on Android
|
||||||
The [Web Share Target API](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) is implemented.
|
The [Web Share Target API](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) is implemented.
|
||||||
|
|
||||||
When the PWA is installed, it will register itself to the share-menu of the device automatically.
|
When the PWA is installed, it will register itself to the share-menu of the device automatically.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
## Send directly via command-line interface
|
## Send directly via command-line interface
|
||||||
Send files or text with PairDrop via command-line interface.
|
Send files or text with PairDrop via command-line interface. \
|
||||||
|
|
||||||
This opens PairDrop in the default browser where you can choose the receiver.
|
This opens PairDrop in the default browser where you can choose the receiver.
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
```bash
|
```bash
|
||||||
$ pairdrop -h
|
pairdrop -h
|
||||||
Current domain: https://pairdrop.net/
|
```
|
||||||
|
```bash
|
||||||
|
Send files or text with PairDrop via command-line interface.
|
||||||
|
Current domain: https://pairdrop-dev.onrender.com/
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
Open PairDrop: pairdrop
|
Open PairDrop: pairdrop
|
||||||
Send files: pairdrop file/directory
|
Send files: pairdrop file1/directory1 (file2/directory2 file3/directory3 ...)
|
||||||
Send text: pairdrop -t "text"
|
Send text: pairdrop -t "text"
|
||||||
Specify domain: pairdrop -d "https://pairdrop.net/"
|
Specify domain: pairdrop -d "https://pairdrop.net/"
|
||||||
Show this help text: pairdrop (-h|--help)
|
Show this help text: pairdrop (-h|--help)
|
||||||
|
|
||||||
|
This pairdrop-cli version was released alongside v1.10.0
|
||||||
```
|
```
|
||||||
|
|
||||||
On Windows Command Prompt you need to use bash: `bash pairdrop -h`
|
<br>
|
||||||
|
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
Download the bash file: [pairdrop-cli/pairdrop](/pairdrop-cli/pairdrop).
|
Download the bash file: [pairdrop-cli/pairdrop](/pairdrop-cli/pairdrop).
|
||||||
|
|
||||||
#### Linux
|
#### Linux
|
||||||
1. Put the file in a preferred folder e.g. `/usr/local/bin`
|
1. Download the latest _pairdrop-cli.zip_ from the [releases page](https://github.com/schlagmichdoch/PairDrop/releases)
|
||||||
2. Make sure the bash file is executable. Otherwise, use `chmod +x pairdrop`
|
2. Unzip the archive to a folder of your choice e.g. `/usr/local/bin/pairdrop-cli/`
|
||||||
3. Add absolute path of the folder to PATH variable to make `pairdrop` available globally by executing
|
3. Make sure the bash file `/usr/local/bin/pairdrop-cli/pairdrop` is executable. Otherwise, use `chmod +x pairdrop`
|
||||||
`export PATH=$PATH:/opt/pairdrop-cli`
|
4. Add absolute path of the folder to PATH variable to make `pairdrop` available globally by executing
|
||||||
|
`export PATH=$PATH:/usr/local/bin/pairdrop-cli/`
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
#### Mac
|
#### Mac
|
||||||
1. add bash file to `/usr/local/bin`
|
1. add bash file to `/usr/local/bin`
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
1. Put file in a preferred folder e.g. `C:\Users\Public\pairdrop-cli`
|
1. Download the latest _pairdrop-cli.zip_ from the [releases page](https://github.com/schlagmichdoch/PairDrop/releases)
|
||||||
2. Search for and open `Edit environment variables for your account`
|
2. Put file in a preferred folder e.g. `C:\Program Files\pairdrop-cli`
|
||||||
3. Click `Environment Variables…`
|
3. Search for and open `Edit environment variables for your account`
|
||||||
4. Under *System Variables* select `Path` and click *Edit...*
|
4. Click `Environment Variables…`
|
||||||
5. Click *New*, insert the preferred folder (`C:\Users\Public\pairdrop-cli`), click *OK* until all windows are closed
|
5. Under *System Variables* select `Path` and click *Edit...*
|
||||||
6. Reopen Command prompt window
|
6. Click *New*, insert the preferred folder (`C:\Program Files\pairdrop-cli`), click *OK* until all windows are closed
|
||||||
|
7. Reopen Command prompt window
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
As Windows cannot execute bash scripts natively, you need to install [Git Bash](https://gitforwindows.org/).
|
||||||
|
Then, you can also use pairdrop-cli from the default Windows Command Prompt \
|
||||||
|
by using the shell file instead of the bash file: `pairdrop.sh -h` which then itself executes \
|
||||||
|
pairdrop-cli (the bash file) via the Git Bash.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Send multiple files and directories directly from context menu on Windows
|
||||||
|
|
||||||
|
### Registering to open files with PairDrop
|
||||||
|
It is possible to send multiple files with PairDrop via the context menu by adding pairdrop-cli to Windows `Send to` menu:
|
||||||
|
1. Download the latest _pairdrop-cli.zip_ from the [releases page](https://github.com/schlagmichdoch/PairDrop/releases)
|
||||||
|
2. Unzip the archive to a folder of your choice e.g. `C:\Program Files\pairdrop-cli\`
|
||||||
|
3. Copy the shortcut _send with PairDrop.lnk_
|
||||||
|
4. Hit Windows Key+R, type: `shell:sendto` and hit Enter.
|
||||||
|
5. Paste the copied shortcut into the directory
|
||||||
|
6. Open the properties window of the shortcut and edit the link field to point to _send-with-pairdrop.ps1_ located in the folder you used in step 2: \
|
||||||
|
`"C:\Program Files\PowerShell\7\pwsh.exe" -File "C:\Program Files\pairdrop-cli\send-with-pairdrop.ps1"`
|
||||||
|
7. You are done! You can now send multiple files and directories directly via PairDrop:
|
||||||
|
|
||||||
|
> _context menu > Send to > PairDrop_
|
||||||
|
|
||||||
|
##### Requirements
|
||||||
|
As Windows cannot execute bash scripts natively, you need to install [Git Bash](https://gitforwindows.org/).
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Send multiple files and directories directly from context menu on Ubuntu using Nautilus
|
||||||
|
|
||||||
|
### Registering to open files with PairDrop
|
||||||
|
It is possible to send multiple files with PairDrop via the context menu by adding pairdrop-cli to Nautilus `Scripts` menu:
|
||||||
|
1. Download the latest _pairdrop-cli.zip_ from the [releases page](https://github.com/schlagmichdoch/PairDrop/releases)
|
||||||
|
2. Unzip the archive to a folder of your choice e.g. `/usr/local/bin/pairdrop-cli/`
|
||||||
|
3. Copy the shell file _send-with-pairdrop.sh_ to `/home/<user>/.local/share/nautilus/scripts/`
|
||||||
|
4. Edit the shell file and edit the variable `pathToPairDropCli` to point to the pairdrop-cli executable from step 2 (e.g. `/usr/local/bin/pairdrop-cli/pairdrop`)
|
||||||
|
5. Make sure the shell file `/home/<user>/.local/share/nautilus/scripts/send-with-pairdrop.sh` is executable. Otherwise, use `chmod +x send-with-pairdrop.sh`
|
||||||
|
6. You are done! You can now send multiple files and directories directly via PairDrop:
|
||||||
|
|
||||||
|
> _context menu > Scripts > send-with-pairdrop.sh_
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## File Handling API
|
||||||
|
The [File Handling API](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/handle-files)
|
||||||
|
was implemented, but it was removed as default file associations were overwritten ([#17](https://github.com/schlagmichdoch/PairDrop/issues/17),
|
||||||
|
[#116](https://github.com/schlagmichdoch/PairDrop/issues/116) [#190](https://github.com/schlagmichdoch/PairDrop/issues/190))
|
||||||
|
and it only worked with explicitly specified file types and not with directories at all.
|
||||||
|
|
||||||
[< Back](/README.md)
|
[< Back](/README.md)
|
||||||
|
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 1.5 MiB |
852
index.js
|
@ -1,852 +0,0 @@
|
||||||
const process = require('process')
|
|
||||||
const crypto = require('crypto')
|
|
||||||
const {spawn} = require('child_process')
|
|
||||||
const WebSocket = require('ws');
|
|
||||||
const fs = require('fs');
|
|
||||||
const parser = require('ua-parser-js');
|
|
||||||
const { uniqueNamesGenerator, animals, colors } = require('unique-names-generator');
|
|
||||||
const express = require('express');
|
|
||||||
const RateLimit = require('express-rate-limit');
|
|
||||||
const http = require('http');
|
|
||||||
|
|
||||||
// Handle SIGINT
|
|
||||||
process.on('SIGINT', () => {
|
|
||||||
console.info("SIGINT Received, exiting...")
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Handle SIGTERM
|
|
||||||
process.on('SIGTERM', () => {
|
|
||||||
console.info("SIGTERM Received, exiting...")
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Handle APP ERRORS
|
|
||||||
process.on('uncaughtException', (error, origin) => {
|
|
||||||
console.log('----- Uncaught exception -----')
|
|
||||||
console.log(error)
|
|
||||||
console.log('----- Exception origin -----')
|
|
||||||
console.log(origin)
|
|
||||||
})
|
|
||||||
process.on('unhandledRejection', (reason, promise) => {
|
|
||||||
console.log('----- Unhandled Rejection at -----')
|
|
||||||
console.log(promise)
|
|
||||||
console.log('----- Reason -----')
|
|
||||||
console.log(reason)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (process.argv.includes('--auto-restart')) {
|
|
||||||
process.on(
|
|
||||||
'uncaughtException',
|
|
||||||
() => {
|
|
||||||
process.once(
|
|
||||||
'exit',
|
|
||||||
() => spawn(
|
|
||||||
process.argv.shift(),
|
|
||||||
process.argv,
|
|
||||||
{
|
|
||||||
cwd: process.cwd(),
|
|
||||||
detached: true,
|
|
||||||
stdio: 'inherit'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
process.exit();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rtcConfig = process.env.RTC_CONFIG
|
|
||||||
? JSON.parse(fs.readFileSync(process.env.RTC_CONFIG, 'utf8'))
|
|
||||||
: {
|
|
||||||
"sdpSemantics": "unified-plan",
|
|
||||||
"iceServers": [
|
|
||||||
{
|
|
||||||
"urls": "stun:stun.l.google.com:19302"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
if (process.argv.includes('--rate-limit')) {
|
|
||||||
const limiter = RateLimit({
|
|
||||||
windowMs: 5 * 60 * 1000, // 5 minutes
|
|
||||||
max: 1000, // Limit each IP to 1000 requests per `window` (here, per 5 minutes)
|
|
||||||
message: 'Too many requests from this IP Address, please try again after 5 minutes.',
|
|
||||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
|
||||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use(limiter);
|
|
||||||
// ensure correct client ip and not the ip of the reverse proxy is used for rate limiting on render.com
|
|
||||||
// see https://github.com/express-rate-limit/express-rate-limit#troubleshooting-proxy-issues
|
|
||||||
app.set('trust proxy', 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.argv.includes('--include-ws-fallback')) {
|
|
||||||
app.use(express.static('public_included_ws_fallback'));
|
|
||||||
} else {
|
|
||||||
app.use(express.static('public'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const debugMode = process.env.DEBUG_MODE === "true";
|
|
||||||
|
|
||||||
if (debugMode) {
|
|
||||||
console.log("DEBUG_MODE is active. To protect privacy, do not use in production.")
|
|
||||||
}
|
|
||||||
|
|
||||||
let ipv6_lcl;
|
|
||||||
if (process.env.IPV6_LOCALIZE) {
|
|
||||||
ipv6_lcl = parseInt(process.env.IPV6_LOCALIZE);
|
|
||||||
if (!ipv6_lcl || !(0 < ipv6_lcl && ipv6_lcl < 8)) {
|
|
||||||
console.error("IPV6_LOCALIZE must be an integer between 1 and 7");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("IPv6 client IPs will be localized to", ipv6_lcl, ipv6_lcl === 1 ? "segment" : "segments");
|
|
||||||
}
|
|
||||||
|
|
||||||
app.use(function(req, res) {
|
|
||||||
res.redirect('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
res.sendFile('index.html');
|
|
||||||
});
|
|
||||||
|
|
||||||
const server = http.createServer(app);
|
|
||||||
const port = process.env.PORT || 3000;
|
|
||||||
|
|
||||||
if (process.argv.includes('--localhost-only')) {
|
|
||||||
server.listen(port, '127.0.0.1');
|
|
||||||
} else {
|
|
||||||
server.listen(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
server.on('error', (err) => {
|
|
||||||
if (err.code === 'EADDRINUSE') {
|
|
||||||
console.error(err);
|
|
||||||
console.info("Error EADDRINUSE received, exiting process without restarting process...");
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
class PairDropServer {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this._wss = new WebSocket.Server({ server });
|
|
||||||
this._wss.on('connection', (socket, request) => this._onConnection(new Peer(socket, request)));
|
|
||||||
|
|
||||||
this._rooms = {}; // { roomId: peers[] }
|
|
||||||
this._roomSecrets = {}; // { pairKey: roomSecret }
|
|
||||||
|
|
||||||
this._keepAliveTimers = {};
|
|
||||||
|
|
||||||
console.log('PairDrop is running on port', port);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onConnection(peer) {
|
|
||||||
peer.socket.on('message', message => this._onMessage(peer, message));
|
|
||||||
peer.socket.onerror = e => console.error(e);
|
|
||||||
|
|
||||||
this._keepAlive(peer);
|
|
||||||
|
|
||||||
this._send(peer, {
|
|
||||||
type: 'rtc-config',
|
|
||||||
config: rtcConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
// send displayName
|
|
||||||
this._send(peer, {
|
|
||||||
type: 'display-name',
|
|
||||||
message: {
|
|
||||||
displayName: peer.name.displayName,
|
|
||||||
deviceName: peer.name.deviceName,
|
|
||||||
peerId: peer.id,
|
|
||||||
peerIdHash: hasher.hashCodeSalted(peer.id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMessage(sender, message) {
|
|
||||||
// Try to parse message
|
|
||||||
try {
|
|
||||||
message = JSON.parse(message);
|
|
||||||
} catch (e) {
|
|
||||||
return; // TODO: handle malformed JSON
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (message.type) {
|
|
||||||
case 'disconnect':
|
|
||||||
this._onDisconnect(sender);
|
|
||||||
break;
|
|
||||||
case 'pong':
|
|
||||||
this._setKeepAliveTimerToNow(sender);
|
|
||||||
break;
|
|
||||||
case 'join-ip-room':
|
|
||||||
this._joinIpRoom(sender);
|
|
||||||
break;
|
|
||||||
case 'room-secrets':
|
|
||||||
this._onRoomSecrets(sender, message);
|
|
||||||
break;
|
|
||||||
case 'room-secrets-deleted':
|
|
||||||
this._onRoomSecretsDeleted(sender, message);
|
|
||||||
break;
|
|
||||||
case 'pair-device-initiate':
|
|
||||||
this._onPairDeviceInitiate(sender);
|
|
||||||
break;
|
|
||||||
case 'pair-device-join':
|
|
||||||
this._onPairDeviceJoin(sender, message);
|
|
||||||
break;
|
|
||||||
case 'pair-device-cancel':
|
|
||||||
this._onPairDeviceCancel(sender);
|
|
||||||
break;
|
|
||||||
case 'regenerate-room-secret':
|
|
||||||
this._onRegenerateRoomSecret(sender, message);
|
|
||||||
break;
|
|
||||||
case 'create-public-room':
|
|
||||||
this._onCreatePublicRoom(sender);
|
|
||||||
break;
|
|
||||||
case 'join-public-room':
|
|
||||||
this._onJoinPublicRoom(sender, message);
|
|
||||||
break;
|
|
||||||
case 'leave-public-room':
|
|
||||||
this._onLeavePublicRoom(sender);
|
|
||||||
break;
|
|
||||||
case 'signal':
|
|
||||||
default:
|
|
||||||
this._signalAndRelay(sender, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_signalAndRelay(sender, message) {
|
|
||||||
const room = message.roomType === 'ip'
|
|
||||||
? sender.ip
|
|
||||||
: message.roomId;
|
|
||||||
|
|
||||||
// relay message to recipient
|
|
||||||
if (message.to && Peer.isValidUuid(message.to) && this._rooms[room]) {
|
|
||||||
const recipient = this._rooms[room][message.to];
|
|
||||||
delete message.to;
|
|
||||||
// add sender
|
|
||||||
message.sender = {
|
|
||||||
id: sender.id,
|
|
||||||
rtcSupported: sender.rtcSupported
|
|
||||||
};
|
|
||||||
this._send(recipient, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDisconnect(sender) {
|
|
||||||
this._disconnect(sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
_disconnect(sender) {
|
|
||||||
this._removePairKey(sender.pairKey);
|
|
||||||
sender.pairKey = null;
|
|
||||||
|
|
||||||
this._cancelKeepAlive(sender);
|
|
||||||
delete this._keepAliveTimers[sender.id];
|
|
||||||
|
|
||||||
this._leaveIpRoom(sender, true);
|
|
||||||
this._leaveAllSecretRooms(sender, true);
|
|
||||||
this._leavePublicRoom(sender, true);
|
|
||||||
|
|
||||||
sender.socket.terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
_onRoomSecrets(sender, message) {
|
|
||||||
if (!message.roomSecrets) return;
|
|
||||||
|
|
||||||
const roomSecrets = message.roomSecrets.filter(roomSecret => {
|
|
||||||
return /^[\x00-\x7F]{64,256}$/.test(roomSecret);
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!roomSecrets) return;
|
|
||||||
|
|
||||||
this._joinSecretRooms(sender, roomSecrets);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onRoomSecretsDeleted(sender, message) {
|
|
||||||
for (let i = 0; i<message.roomSecrets.length; i++) {
|
|
||||||
this._deleteSecretRoom(message.roomSecrets[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_deleteSecretRoom(roomSecret) {
|
|
||||||
const room = this._rooms[roomSecret];
|
|
||||||
if (!room) return;
|
|
||||||
|
|
||||||
for (const peerId in room) {
|
|
||||||
const peer = room[peerId];
|
|
||||||
|
|
||||||
this._leaveSecretRoom(peer, roomSecret, true);
|
|
||||||
|
|
||||||
this._send(peer, {
|
|
||||||
type: 'secret-room-deleted',
|
|
||||||
roomSecret: roomSecret,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onPairDeviceInitiate(sender) {
|
|
||||||
let roomSecret = randomizer.getRandomString(256);
|
|
||||||
let pairKey = this._createPairKey(sender, roomSecret);
|
|
||||||
|
|
||||||
if (sender.pairKey) {
|
|
||||||
this._removePairKey(sender.pairKey);
|
|
||||||
}
|
|
||||||
sender.pairKey = pairKey;
|
|
||||||
|
|
||||||
this._send(sender, {
|
|
||||||
type: 'pair-device-initiated',
|
|
||||||
roomSecret: roomSecret,
|
|
||||||
pairKey: pairKey
|
|
||||||
});
|
|
||||||
this._joinSecretRoom(sender, roomSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onPairDeviceJoin(sender, message) {
|
|
||||||
if (sender.rateLimitReached()) {
|
|
||||||
this._send(sender, { type: 'join-key-rate-limit' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._roomSecrets[message.pairKey] || sender.id === this._roomSecrets[message.pairKey].creator.id) {
|
|
||||||
this._send(sender, { type: 'pair-device-join-key-invalid' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const roomSecret = this._roomSecrets[message.pairKey].roomSecret;
|
|
||||||
const creator = this._roomSecrets[message.pairKey].creator;
|
|
||||||
this._removePairKey(message.pairKey);
|
|
||||||
this._send(sender, {
|
|
||||||
type: 'pair-device-joined',
|
|
||||||
roomSecret: roomSecret,
|
|
||||||
peerId: creator.id
|
|
||||||
});
|
|
||||||
this._send(creator, {
|
|
||||||
type: 'pair-device-joined',
|
|
||||||
roomSecret: roomSecret,
|
|
||||||
peerId: sender.id
|
|
||||||
});
|
|
||||||
this._joinSecretRoom(sender, roomSecret);
|
|
||||||
this._removePairKey(sender.pairKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onPairDeviceCancel(sender) {
|
|
||||||
const pairKey = sender.pairKey
|
|
||||||
|
|
||||||
if (!pairKey) return;
|
|
||||||
|
|
||||||
this._removePairKey(pairKey);
|
|
||||||
this._send(sender, {
|
|
||||||
type: 'pair-device-canceled',
|
|
||||||
pairKey: pairKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onCreatePublicRoom(sender) {
|
|
||||||
let publicRoomId = randomizer.getRandomString(5, true).toLowerCase();
|
|
||||||
|
|
||||||
this._send(sender, {
|
|
||||||
type: 'public-room-created',
|
|
||||||
roomId: publicRoomId
|
|
||||||
});
|
|
||||||
|
|
||||||
this._joinPublicRoom(sender, publicRoomId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onJoinPublicRoom(sender, message) {
|
|
||||||
if (sender.rateLimitReached()) {
|
|
||||||
this._send(sender, { type: 'join-key-rate-limit' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._rooms[message.publicRoomId] && !message.createIfInvalid) {
|
|
||||||
this._send(sender, { type: 'public-room-id-invalid', publicRoomId: message.publicRoomId });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._leavePublicRoom(sender);
|
|
||||||
this._joinPublicRoom(sender, message.publicRoomId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onLeavePublicRoom(sender) {
|
|
||||||
this._leavePublicRoom(sender, true);
|
|
||||||
this._send(sender, { type: 'public-room-left' });
|
|
||||||
}
|
|
||||||
|
|
||||||
_onRegenerateRoomSecret(sender, message) {
|
|
||||||
const oldRoomSecret = message.roomSecret;
|
|
||||||
const newRoomSecret = randomizer.getRandomString(256);
|
|
||||||
|
|
||||||
// notify all other peers
|
|
||||||
for (const peerId in this._rooms[oldRoomSecret]) {
|
|
||||||
const peer = this._rooms[oldRoomSecret][peerId];
|
|
||||||
this._send(peer, {
|
|
||||||
type: 'room-secret-regenerated',
|
|
||||||
oldRoomSecret: oldRoomSecret,
|
|
||||||
newRoomSecret: newRoomSecret,
|
|
||||||
});
|
|
||||||
peer.removeRoomSecret(oldRoomSecret);
|
|
||||||
}
|
|
||||||
delete this._rooms[oldRoomSecret];
|
|
||||||
}
|
|
||||||
|
|
||||||
_createPairKey(creator, roomSecret) {
|
|
||||||
let pairKey;
|
|
||||||
do {
|
|
||||||
// get randomInt until keyRoom not occupied
|
|
||||||
pairKey = crypto.randomInt(1000000, 1999999).toString().substring(1); // include numbers with leading 0s
|
|
||||||
} while (pairKey in this._roomSecrets)
|
|
||||||
|
|
||||||
this._roomSecrets[pairKey] = {
|
|
||||||
roomSecret: roomSecret,
|
|
||||||
creator: creator
|
|
||||||
}
|
|
||||||
|
|
||||||
return pairKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
_removePairKey(roomKey) {
|
|
||||||
if (roomKey in this._roomSecrets) {
|
|
||||||
this._roomSecrets[roomKey].creator.roomKey = null
|
|
||||||
delete this._roomSecrets[roomKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_joinIpRoom(peer) {
|
|
||||||
this._joinRoom(peer, 'ip', peer.ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
_joinSecretRoom(peer, roomSecret) {
|
|
||||||
this._joinRoom(peer, 'secret', roomSecret);
|
|
||||||
|
|
||||||
// add secret to peer
|
|
||||||
peer.addRoomSecret(roomSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
_joinPublicRoom(peer, publicRoomId) {
|
|
||||||
// prevent joining of 2 public rooms simultaneously
|
|
||||||
this._leavePublicRoom(peer);
|
|
||||||
|
|
||||||
this._joinRoom(peer, 'public-id', publicRoomId);
|
|
||||||
|
|
||||||
peer.publicRoomId = publicRoomId;
|
|
||||||
}
|
|
||||||
|
|
||||||
_joinRoom(peer, roomType, roomId) {
|
|
||||||
// roomType: 'ip', 'secret' or 'public-id'
|
|
||||||
if (this._rooms[roomId] && this._rooms[roomId][peer.id]) {
|
|
||||||
// ensures that otherPeers never receive `peer-left` after `peer-joined` on reconnect.
|
|
||||||
this._leaveRoom(peer, roomType, roomId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if room doesn't exist, create it
|
|
||||||
if (!this._rooms[roomId]) {
|
|
||||||
this._rooms[roomId] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
this._notifyPeers(peer, roomType, roomId);
|
|
||||||
|
|
||||||
// add peer to room
|
|
||||||
this._rooms[roomId][peer.id] = peer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_leaveIpRoom(peer, disconnect = false) {
|
|
||||||
this._leaveRoom(peer, 'ip', peer.ip, disconnect);
|
|
||||||
}
|
|
||||||
|
|
||||||
_leaveSecretRoom(peer, roomSecret, disconnect = false) {
|
|
||||||
this._leaveRoom(peer, 'secret', roomSecret, disconnect)
|
|
||||||
|
|
||||||
//remove secret from peer
|
|
||||||
peer.removeRoomSecret(roomSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
_leavePublicRoom(peer, disconnect = false) {
|
|
||||||
if (!peer.publicRoomId) return;
|
|
||||||
|
|
||||||
this._leaveRoom(peer, 'public-id', peer.publicRoomId, disconnect);
|
|
||||||
|
|
||||||
peer.publicRoomId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_leaveRoom(peer, roomType, roomId, disconnect = false) {
|
|
||||||
if (!this._rooms[roomId] || !this._rooms[roomId][peer.id]) return;
|
|
||||||
|
|
||||||
// remove peer from room
|
|
||||||
delete this._rooms[roomId][peer.id];
|
|
||||||
|
|
||||||
// delete room if empty and abort
|
|
||||||
if (!Object.keys(this._rooms[roomId]).length) {
|
|
||||||
delete this._rooms[roomId];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// notify all other peers that remain in room that peer left
|
|
||||||
for (const otherPeerId in this._rooms[roomId]) {
|
|
||||||
const otherPeer = this._rooms[roomId][otherPeerId];
|
|
||||||
|
|
||||||
let msg = {
|
|
||||||
type: 'peer-left',
|
|
||||||
peerId: peer.id,
|
|
||||||
roomType: roomType,
|
|
||||||
roomId: roomId,
|
|
||||||
disconnect: disconnect
|
|
||||||
};
|
|
||||||
|
|
||||||
this._send(otherPeer, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_notifyPeers(peer, roomType, roomId) {
|
|
||||||
if (!this._rooms[roomId]) return;
|
|
||||||
|
|
||||||
// notify all other peers that peer joined
|
|
||||||
for (const otherPeerId in this._rooms[roomId]) {
|
|
||||||
if (otherPeerId === peer.id) continue;
|
|
||||||
const otherPeer = this._rooms[roomId][otherPeerId];
|
|
||||||
|
|
||||||
let msg = {
|
|
||||||
type: 'peer-joined',
|
|
||||||
peer: peer.getInfo(),
|
|
||||||
roomType: roomType,
|
|
||||||
roomId: roomId
|
|
||||||
};
|
|
||||||
|
|
||||||
this._send(otherPeer, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// notify peer about peers already in the room
|
|
||||||
const otherPeers = [];
|
|
||||||
for (const otherPeerId in this._rooms[roomId]) {
|
|
||||||
if (otherPeerId === peer.id) continue;
|
|
||||||
otherPeers.push(this._rooms[roomId][otherPeerId].getInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = {
|
|
||||||
type: 'peers',
|
|
||||||
peers: otherPeers,
|
|
||||||
roomType: roomType,
|
|
||||||
roomId: roomId
|
|
||||||
};
|
|
||||||
|
|
||||||
this._send(peer, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
_joinSecretRooms(peer, roomSecrets) {
|
|
||||||
for (let i=0; i<roomSecrets.length; i++) {
|
|
||||||
this._joinSecretRoom(peer, roomSecrets[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_leaveAllSecretRooms(peer, disconnect = false) {
|
|
||||||
for (let i=0; i<peer.roomSecrets.length; i++) {
|
|
||||||
this._leaveSecretRoom(peer, peer.roomSecrets[i], disconnect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_send(peer, message) {
|
|
||||||
if (!peer) return;
|
|
||||||
if (this._wss.readyState !== this._wss.OPEN) return;
|
|
||||||
message = JSON.stringify(message);
|
|
||||||
peer.socket.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
_keepAlive(peer) {
|
|
||||||
this._cancelKeepAlive(peer);
|
|
||||||
let timeout = 1000;
|
|
||||||
|
|
||||||
if (!this._keepAliveTimers[peer.id]) {
|
|
||||||
this._keepAliveTimers[peer.id] = {
|
|
||||||
timer: 0,
|
|
||||||
lastBeat: Date.now()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Date.now() - this._keepAliveTimers[peer.id].lastBeat > 5 * timeout) {
|
|
||||||
// Disconnect peer if unresponsive for 10s
|
|
||||||
this._disconnect(peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._send(peer, { type: 'ping' });
|
|
||||||
|
|
||||||
this._keepAliveTimers[peer.id].timer = setTimeout(() => this._keepAlive(peer), timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
_cancelKeepAlive(peer) {
|
|
||||||
if (this._keepAliveTimers[peer.id]?.timer) {
|
|
||||||
clearTimeout(this._keepAliveTimers[peer.id].timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_setKeepAliveTimerToNow(peer) {
|
|
||||||
if (this._keepAliveTimers[peer.id]?.lastBeat) {
|
|
||||||
this._keepAliveTimers[peer.id].lastBeat = Date.now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Peer {
|
|
||||||
|
|
||||||
constructor(socket, request) {
|
|
||||||
// set socket
|
|
||||||
this.socket = socket;
|
|
||||||
|
|
||||||
// set remote ip
|
|
||||||
this._setIP(request);
|
|
||||||
|
|
||||||
// set peer id
|
|
||||||
this._setPeerId(request);
|
|
||||||
|
|
||||||
// is WebRTC supported ?
|
|
||||||
this.rtcSupported = request.url.indexOf('webrtc') > -1;
|
|
||||||
|
|
||||||
// set name
|
|
||||||
this._setName(request);
|
|
||||||
|
|
||||||
this.requestRate = 0;
|
|
||||||
|
|
||||||
this.roomSecrets = [];
|
|
||||||
this.roomKey = null;
|
|
||||||
|
|
||||||
this.publicRoomId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
rateLimitReached() {
|
|
||||||
// rate limit implementation: max 10 attempts every 10s
|
|
||||||
if (this.requestRate >= 10) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
this.requestRate += 1;
|
|
||||||
setTimeout(_ => this.requestRate -= 1, 10000);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setIP(request) {
|
|
||||||
if (request.headers['cf-connecting-ip']) {
|
|
||||||
this.ip = request.headers['cf-connecting-ip'].split(/\s*,\s*/)[0];
|
|
||||||
} else if (request.headers['x-forwarded-for']) {
|
|
||||||
this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
|
|
||||||
} else {
|
|
||||||
this.ip = request.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the prefix used for IPv4-translated addresses
|
|
||||||
if (this.ip.substring(0,7) === "::ffff:")
|
|
||||||
this.ip = this.ip.substring(7);
|
|
||||||
|
|
||||||
let ipv6_was_localized = false;
|
|
||||||
if (ipv6_lcl && this.ip.includes(':')) {
|
|
||||||
this.ip = this.ip.split(':',ipv6_lcl).join(':');
|
|
||||||
ipv6_was_localized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debugMode) {
|
|
||||||
console.debug("----DEBUGGING-PEER-IP-START----");
|
|
||||||
console.debug("remoteAddress:", request.connection.remoteAddress);
|
|
||||||
console.debug("x-forwarded-for:", request.headers['x-forwarded-for']);
|
|
||||||
console.debug("cf-connecting-ip:", request.headers['cf-connecting-ip']);
|
|
||||||
if (ipv6_was_localized)
|
|
||||||
console.debug("IPv6 client IP was localized to", ipv6_lcl, ipv6_lcl > 1 ? "segments" : "segment");
|
|
||||||
console.debug("PairDrop uses:", this.ip);
|
|
||||||
console.debug("IP is private:", this.ipIsPrivate(this.ip));
|
|
||||||
console.debug("if IP is private, '127.0.0.1' is used instead");
|
|
||||||
console.debug("----DEBUGGING-PEER-IP-END----");
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPv4 and IPv6 use different values to refer to localhost
|
|
||||||
// put all peers on the same network as the server into the same room as well
|
|
||||||
if (this.ip === '::1' || this.ipIsPrivate(this.ip)) {
|
|
||||||
this.ip = '127.0.0.1';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ipIsPrivate(ip) {
|
|
||||||
// if ip is IPv4
|
|
||||||
if (!ip.includes(":")) {
|
|
||||||
// 10.0.0.0 - 10.255.255.255 || 172.16.0.0 - 172.31.255.255 || 192.168.0.0 - 192.168.255.255
|
|
||||||
return /^(10)\.(.*)\.(.*)\.(.*)$/.test(ip) || /^(172)\.(1[6-9]|2[0-9]|3[0-1])\.(.*)\.(.*)$/.test(ip) || /^(192)\.(168)\.(.*)\.(.*)$/.test(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// else: ip is IPv6
|
|
||||||
const firstWord = ip.split(":").find(el => !!el); //get first not empty word
|
|
||||||
|
|
||||||
// The original IPv6 Site Local addresses (fec0::/10) are deprecated. Range: fec0 - feff
|
|
||||||
if (/^fe[c-f][0-f]$/.test(firstWord))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// These days Unique Local Addresses (ULA) are used in place of Site Local.
|
|
||||||
// Range: fc00 - fcff
|
|
||||||
else if (/^fc[0-f]{2}$/.test(firstWord))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Range: fd00 - fcff
|
|
||||||
else if (/^fd[0-f]{2}$/.test(firstWord))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Link local addresses (prefixed with fe80) are not routable
|
|
||||||
else if (firstWord === "fe80")
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Discard Prefix
|
|
||||||
else if (firstWord === "100")
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Any other IP address is not Unique Local Address (ULA)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setPeerId(request) {
|
|
||||||
const searchParams = new URL(request.url, "http://server").searchParams;
|
|
||||||
let peerId = searchParams.get("peer_id");
|
|
||||||
let peerIdHash = searchParams.get("peer_id_hash");
|
|
||||||
if (peerId && Peer.isValidUuid(peerId) && this.isPeerIdHashValid(peerId, peerIdHash)) {
|
|
||||||
this.id = peerId;
|
|
||||||
} else {
|
|
||||||
this.id = crypto.randomUUID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
return `<Peer id=${this.id} ip=${this.ip} rtcSupported=${this.rtcSupported}>`
|
|
||||||
}
|
|
||||||
|
|
||||||
_setName(req) {
|
|
||||||
let ua = parser(req.headers['user-agent']);
|
|
||||||
|
|
||||||
|
|
||||||
let deviceName = '';
|
|
||||||
|
|
||||||
if (ua.os && ua.os.name) {
|
|
||||||
deviceName = ua.os.name.replace('Mac OS', 'Mac') + ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ua.device.model) {
|
|
||||||
deviceName += ua.device.model;
|
|
||||||
} else {
|
|
||||||
deviceName += ua.browser.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!deviceName)
|
|
||||||
deviceName = 'Unknown Device';
|
|
||||||
|
|
||||||
const displayName = uniqueNamesGenerator({
|
|
||||||
length: 2,
|
|
||||||
separator: ' ',
|
|
||||||
dictionaries: [colors, animals],
|
|
||||||
style: 'capital',
|
|
||||||
seed: cyrb53(this.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.name = {
|
|
||||||
model: ua.device.model,
|
|
||||||
os: ua.os.name,
|
|
||||||
browser: ua.browser.name,
|
|
||||||
type: ua.device.type,
|
|
||||||
deviceName,
|
|
||||||
displayName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getInfo() {
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
name: this.name,
|
|
||||||
rtcSupported: this.rtcSupported
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static isValidUuid(uuid) {
|
|
||||||
return /^([0-9]|[a-f]){8}-(([0-9]|[a-f]){4}-){3}([0-9]|[a-f]){12}$/.test(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
isPeerIdHashValid(peerId, peerIdHash) {
|
|
||||||
return peerIdHash === hasher.hashCodeSalted(peerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
addRoomSecret(roomSecret) {
|
|
||||||
if (!(roomSecret in this.roomSecrets)) {
|
|
||||||
this.roomSecrets.push(roomSecret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeRoomSecret(roomSecret) {
|
|
||||||
if (roomSecret in this.roomSecrets) {
|
|
||||||
delete this.roomSecrets[roomSecret];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasher = (() => {
|
|
||||||
let password;
|
|
||||||
return {
|
|
||||||
hashCodeSalted(salt) {
|
|
||||||
if (!password) {
|
|
||||||
// password is created on first call.
|
|
||||||
password = randomizer.getRandomString(128);
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.createHash("sha3-512")
|
|
||||||
.update(password)
|
|
||||||
.update(crypto.createHash("sha3-512").update(salt, "utf8").digest("hex"))
|
|
||||||
.digest("hex");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
const randomizer = (() => {
|
|
||||||
let charCodeLettersOnly = r => 65 <= r && r <= 90;
|
|
||||||
let charCodeAllPrintableChars = r => r === 45 || 47 <= r && r <= 57 || 64 <= r && r <= 90 || 97 <= r && r <= 122;
|
|
||||||
|
|
||||||
return {
|
|
||||||
getRandomString(length, lettersOnly = false) {
|
|
||||||
const charCodeCondition = lettersOnly
|
|
||||||
? charCodeLettersOnly
|
|
||||||
: charCodeAllPrintableChars;
|
|
||||||
|
|
||||||
let string = "";
|
|
||||||
while (string.length < length) {
|
|
||||||
let arr = new Uint16Array(length);
|
|
||||||
crypto.webcrypto.getRandomValues(arr);
|
|
||||||
arr = Array.apply([], arr); /* turn into non-typed array */
|
|
||||||
arr = arr.map(function (r) {
|
|
||||||
return r % 128
|
|
||||||
})
|
|
||||||
arr = arr.filter(function (r) {
|
|
||||||
/* strip non-printables: if we transform into desirable range we have a probability bias, so I suppose we better skip this character */
|
|
||||||
return charCodeCondition(r);
|
|
||||||
});
|
|
||||||
string += String.fromCharCode.apply(String, arr);
|
|
||||||
}
|
|
||||||
return string.substring(0, length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
/*
|
|
||||||
cyrb53 (c) 2018 bryc (github.com/bryc)
|
|
||||||
A fast and simple hash function with decent collision resistance.
|
|
||||||
Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
|
|
||||||
Public domain. Attribution appreciated.
|
|
||||||
*/
|
|
||||||
const cyrb53 = function(str, seed = 0) {
|
|
||||||
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
|
||||||
for (let i = 0, ch; i < str.length; i++) {
|
|
||||||
ch = str.charCodeAt(i);
|
|
||||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
||||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
||||||
}
|
|
||||||
h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909);
|
|
||||||
h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909);
|
|
||||||
return 4294967296 * (2097151 & h2) + (h1>>>0);
|
|
||||||
};
|
|
||||||
|
|
||||||
new PairDropServer();
|
|
28
licenses/BSD_3-Clause-zip-js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023, Gildas Lormeau
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
licenses/MIT-NoSleep
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Rich Tibbett
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
licenses/MIT-heic2any
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Alex Corvi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "pairdrop",
|
"name": "pairdrop",
|
||||||
"version": "1.9.4",
|
"version": "1.9.4",
|
||||||
|
"type": "module",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "server/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js",
|
"start": "node server/index.js",
|
||||||
"start:prod": "node index.js --rate-limit --auto-restart"
|
"start:prod": "node server/index.js --rate-limit --auto-restart"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
|
1
pairdrop-cli/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.pairdrop-cli-config
|
|
@ -12,10 +12,12 @@ help()
|
||||||
echo
|
echo
|
||||||
echo "Usage:"
|
echo "Usage:"
|
||||||
echo -e "Open PairDrop:\t\t$(basename "$0")"
|
echo -e "Open PairDrop:\t\t$(basename "$0")"
|
||||||
echo -e "Send files:\t\t$(basename "$0") file/directory"
|
echo -e "Send files:\t\t$(basename "$0") file1/directory1 (file2/directory2 file3/directory3 ...)"
|
||||||
echo -e "Send text:\t\t$(basename "$0") -t \"text\""
|
echo -e "Send text:\t\t$(basename "$0") -t \"text\""
|
||||||
echo -e "Specify domain:\t\t$(basename "$0") -d \"https://pairdrop.net/\""
|
echo -e "Specify domain:\t\t$(basename "$0") -d \"https://pairdrop.net/\""
|
||||||
echo -e "Show this help text:\t$(basename "$0") (-h|--help)"
|
echo -e "Show this help text:\t$(basename "$0") (-h|--help)"
|
||||||
|
echo
|
||||||
|
echo "This pairdrop-cli version was released alongside v1.10.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
openPairDrop()
|
openPairDrop()
|
||||||
|
@ -36,7 +38,7 @@ openPairDrop()
|
||||||
elif [[ $OS == "WSL" || $OS == "WSL2" ]];then
|
elif [[ $OS == "WSL" || $OS == "WSL2" ]];then
|
||||||
powershell.exe /c "Start-Process ${url}"
|
powershell.exe /c "Start-Process ${url}"
|
||||||
else
|
else
|
||||||
xdg-open "$url"
|
xdg-open "$url" > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ setOs()
|
||||||
specifyDomain()
|
specifyDomain()
|
||||||
{
|
{
|
||||||
[[ ! $1 = http* ]] || [[ ! $1 = */ ]] && echo "Incorrect format. Specify domain like https://pairdrop.net/" && exit
|
[[ ! $1 = http* ]] || [[ ! $1 = */ ]] && echo "Incorrect format. Specify domain like https://pairdrop.net/" && exit
|
||||||
echo "DOMAIN=${1}" > "$CONFIGPATH"
|
echo "DOMAIN=${1}" > "$config_path"
|
||||||
echo -e "Domain is now set to:\n$1\n"
|
echo -e "Domain is now set to:\n$1\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,75 +89,228 @@ sendText()
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
escapePSPath()
|
||||||
|
{
|
||||||
|
local path=$1
|
||||||
|
|
||||||
|
# escape '[' and ']' with grave accent (`) character
|
||||||
|
pathPS=${path//[/\`[}
|
||||||
|
pathPS=${pathPS//]/\`]}
|
||||||
|
# escape single quote (') with another single quote (')
|
||||||
|
pathPS=${pathPS//\'/\'\'}
|
||||||
|
|
||||||
|
# Convert GitHub bash path "/i/path" to Windows path "I:/path"
|
||||||
|
if [[ $pathPS == /* ]]; then
|
||||||
|
# Remove preceding slash
|
||||||
|
pathPS="${pathPS#/}"
|
||||||
|
# Convert drive letter to uppercase
|
||||||
|
driveLetter=$(echo "${pathPS::1}" | tr '[:lower:]' '[:upper:]')
|
||||||
|
# Put together absolute path as used in Windows
|
||||||
|
pathPS="${driveLetter}:${pathPS:1}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$pathPS"
|
||||||
|
}
|
||||||
|
|
||||||
sendFiles()
|
sendFiles()
|
||||||
{
|
{
|
||||||
params="base64zip=hash"
|
params="base64zip=hash"
|
||||||
if [[ $1 == */ ]]; then
|
workingDir="$(pwd)"
|
||||||
path="${1::-1}"
|
tmpDir="/tmp/pairdrop-cli-temp/"
|
||||||
else
|
tmpDirPS="\$env:TEMP/pairdrop-cli-temp/"
|
||||||
path=$1
|
|
||||||
fi
|
index=0
|
||||||
zipPath="${path}_pairdrop.zip"
|
directoryBaseNamesUnix=()
|
||||||
zipPath=${zipPath// /_}
|
directoryPathsUnix=()
|
||||||
|
filePathsUnix=()
|
||||||
|
directoryCount=0
|
||||||
|
fileCount=0
|
||||||
|
pathsPS=""
|
||||||
|
|
||||||
[[ -a "$zipPath" ]] && echo "Cannot overwrite $zipPath. Please remove first." && exit
|
#create tmp folder if it does not exist already
|
||||||
|
if [[ ! -d "$tmpDir" ]]; then
|
||||||
if [[ -d $path ]]; then
|
mkdir "$tmpDir"
|
||||||
zipPathTemp="${path}_pairdrop_temp.zip"
|
|
||||||
[[ -a "$zipPathTemp" ]] && echo "Cannot overwrite $zipPathTemp. Please remove first." && exit
|
|
||||||
echo "Processing directory..."
|
|
||||||
|
|
||||||
# Create zip files temporarily to send directory
|
|
||||||
if [[ $OS == "Windows" ]];then
|
|
||||||
powershell.exe -Command "Compress-Archive -Path ${path} -DestinationPath ${zipPath}"
|
|
||||||
echo "Compress-Archive -Path ${zipPath} -DestinationPath ${zipPathTemp}"
|
|
||||||
powershell.exe -Command "Compress-Archive -Path ${zipPath} -DestinationPath ${zipPathTemp}"
|
|
||||||
else
|
|
||||||
zip -q -b /tmp/ -r "$zipPath" "$path"
|
|
||||||
zip -q -b /tmp/ "$zipPathTemp" "$zipPath"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $OS == "Mac" ]];then
|
|
||||||
hash=$(base64 -i "$zipPathTemp")
|
|
||||||
else
|
|
||||||
hash=$(base64 -w 0 "$zipPathTemp")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# remove temporary temp file
|
|
||||||
rm "$zipPathTemp"
|
|
||||||
else
|
|
||||||
echo "Processing file..."
|
|
||||||
|
|
||||||
# Create zip file temporarily to send file
|
|
||||||
|
|
||||||
if [[ $OS == "Windows" ]];then
|
|
||||||
powershell.exe -Command "Compress-Archive -Path ${path} -DestinationPath ${zipPath} -CompressionLevel Optimal"
|
|
||||||
else
|
|
||||||
zip -q -b /tmp/ "$zipPath" "$path"
|
|
||||||
fi
|
|
||||||
if [[ $OS == "Mac" ]];then
|
|
||||||
hash=$(base64 -i "$zipPath")
|
|
||||||
else
|
|
||||||
hash=$(base64 -w 0 "$zipPath")
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# remove temporary temp file
|
for arg in "$@"; do
|
||||||
rm "$zipPath"
|
echo "$arg"
|
||||||
|
[[ ! -e "$arg" ]] && echo "The given path $arg does not exist." && exit
|
||||||
|
|
||||||
if [[ $(echo -n "$hash" | wc -m) -gt 32600 ]];then
|
# Remove trailing slash from directory
|
||||||
|
arg="${arg%/}"
|
||||||
|
|
||||||
|
# get absolute path and basename of file/directory
|
||||||
|
absolutePath=$(realpath "$arg")
|
||||||
|
baseName=$(basename "$absolutePath")
|
||||||
|
directoryPath=$(dirname "$absolutePath")
|
||||||
|
|
||||||
|
if [[ -d $absolutePath ]]; then
|
||||||
|
# is directory
|
||||||
|
((directoryCount+=1))
|
||||||
|
# add basename and directory path to arrays
|
||||||
|
directoryBaseNamesUnix+=("$baseName")
|
||||||
|
directoryPathsUnix+=("$directoryPath")
|
||||||
|
else
|
||||||
|
# is file
|
||||||
|
((fileCount+=1))
|
||||||
|
absolutePathUnix=$absolutePath
|
||||||
|
# append new path and separate paths with space
|
||||||
|
filePathsUnix+=("$absolutePathUnix")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prepare paths for PowerShell on Windows
|
||||||
|
if [[ $OS == "Windows" ]];then
|
||||||
|
absolutePathPS=$(escapePSPath "$absolutePath")
|
||||||
|
|
||||||
|
# append new path and separate paths with commas
|
||||||
|
pathsPS+="'${absolutePathPS}', "
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set fileNames on first loop
|
||||||
|
if [[ $index == 0 ]]; then
|
||||||
|
baseNameU=${baseName// /_}
|
||||||
|
|
||||||
|
# Prevent baseNameU being empty for hidden files by removing the preceding dot
|
||||||
|
if [[ $baseNameU == .* ]]; then
|
||||||
|
baseNameU=${baseNameU#.*}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# only use trunk of basename "document.txt" -> "document"
|
||||||
|
baseNameTrunk=${baseNameU%.*}
|
||||||
|
|
||||||
|
# remove all special characters
|
||||||
|
zipName=${baseNameTrunk//[^a-zA-Z0-9_]/}
|
||||||
|
|
||||||
|
zipToSendAbs="${tmpDir}${zipName}_pairdrop.zip"
|
||||||
|
wrapperZipAbs="${tmpDir}${zipName}_pairdrop_wrapper.zip"
|
||||||
|
|
||||||
|
if [[ $OS == "Windows" ]];then
|
||||||
|
zipToSendAbsPS="${tmpDirPS}${zipName}_pairdrop.zip"
|
||||||
|
wrapperZipAbsPS="${tmpDirPS}${zipName}_pairdrop_wrapper.zip"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
((index+=1)) # somehow ((index++)) stops the script
|
||||||
|
done
|
||||||
|
|
||||||
|
# Prepare paths for PowerShell on Windows
|
||||||
|
if [[ $OS == "Windows" ]];then
|
||||||
|
# remove trailing comma
|
||||||
|
pathsPS=${pathsPS%??}
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Preparing ${fileCount} files and ${directoryCount} directories..."
|
||||||
|
|
||||||
|
# if arguments include files only -> zip files once so files it is unzipped by sending browser
|
||||||
|
# if arguments include directories -> wrap first zip in a second wrapper zip so that after unzip by sending browser a zip file is sent to receiver
|
||||||
|
#
|
||||||
|
# Preferred zip structure:
|
||||||
|
# pairdrop "d1/d2/d3/f1" "../../d4/d5/d6/f2" "d7/" "../d8/" "f5"
|
||||||
|
# zip structure: pairdrop.zip
|
||||||
|
# |-f1
|
||||||
|
# |-f2
|
||||||
|
# |-d7/
|
||||||
|
# |-d8/
|
||||||
|
# |-f5
|
||||||
|
# -> truncate (relative) paths but keep directories
|
||||||
|
|
||||||
|
[[ -e "$zipToSendAbs" ]] && echo "Cannot overwrite $zipToSendAbs. Please remove first." && exit
|
||||||
|
|
||||||
|
if [[ $OS == "Windows" ]];then
|
||||||
|
# Powershell does preferred zip structure natively
|
||||||
|
powershell.exe -Command "Compress-Archive -Path ${pathsPS} -DestinationPath ${zipToSendAbsPS}"
|
||||||
|
else
|
||||||
|
# Workaround needed to create preferred zip structure on unix systems
|
||||||
|
# Create zip file with all single files by junking the path
|
||||||
|
if [[ $fileCount != 0 ]]; then
|
||||||
|
zip -q -b /tmp/ -j -0 -r "$zipToSendAbs" "${filePathsUnix[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add directories recursively to zip file
|
||||||
|
index=0
|
||||||
|
while [[ $index < $directoryCount ]]; do
|
||||||
|
# workaround to keep directory name but junk the rest of the paths
|
||||||
|
|
||||||
|
# cd to path above directory
|
||||||
|
cd "${directoryPathsUnix[index]}"
|
||||||
|
|
||||||
|
# add directory to zip without junking the path
|
||||||
|
zip -q -b /tmp/ -0 -u -r "$zipToSendAbs" "${directoryBaseNamesUnix[index]}"
|
||||||
|
|
||||||
|
# cd back to working directory
|
||||||
|
cd "$workingDir"
|
||||||
|
|
||||||
|
((index+=1)) # somehow ((index++)) stops the script
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If directories are included send as zip
|
||||||
|
# -> Create additional zip wrapper which will be unzipped by the sending browser
|
||||||
|
if [[ "$directoryCount" != 0 ]]; then
|
||||||
|
echo "Bundle as ZIP file..."
|
||||||
|
|
||||||
|
# Prevent filename from being absolute zip path by "cd"ing to directory before zipping
|
||||||
|
zipToSendDirectory=$(dirname "$zipToSendAbs")
|
||||||
|
zipToSendBaseName=$(basename "$zipToSendAbs")
|
||||||
|
|
||||||
|
cd "$zipToSendDirectory"
|
||||||
|
|
||||||
|
[[ -e "$wrapperZipAbs" ]] && echo "Cannot overwrite $wrapperZipAbs. Please remove first." && exit
|
||||||
|
|
||||||
|
if [[ $OS == "Windows" ]];then
|
||||||
|
powershell.exe -Command "Compress-Archive -Path ${zipToSendBaseName} -DestinationPath ${wrapperZipAbsPS} -CompressionLevel Optimal"
|
||||||
|
else
|
||||||
|
zip -q -b /tmp/ -0 "$wrapperZipAbs" "$zipToSendBaseName"
|
||||||
|
fi
|
||||||
|
cd "$workingDir"
|
||||||
|
|
||||||
|
# remove inner zip file and set wrapper as zipToSend (do not differentiate between OS as this is done via Git Bash on Windows)
|
||||||
|
rm "$zipToSendAbs"
|
||||||
|
|
||||||
|
zipToSendAbs=$wrapperZipAbs
|
||||||
|
fi
|
||||||
|
|
||||||
|
# base64 encode zip file
|
||||||
|
if [[ $OS == "Mac" ]];then
|
||||||
|
hash=$(base64 -i "$zipToSendAbs")
|
||||||
|
else
|
||||||
|
hash=$(base64 -w 0 "$zipToSendAbs")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# remove zip file (do not differentiate between OS as this is done via Git Bash on Windows)
|
||||||
|
rm "$zipToSendAbs"
|
||||||
|
|
||||||
|
if [[ $(echo -n "$hash" | wc -m) -gt 1000 ]];then
|
||||||
params="base64zip=paste"
|
params="base64zip=paste"
|
||||||
|
|
||||||
|
# Copy $hash to clipboard
|
||||||
if [[ $OS == "Windows" || $OS == "WSL" || $OS == "WSL2" ]];then
|
if [[ $OS == "Windows" || $OS == "WSL" || $OS == "WSL2" ]];then
|
||||||
echo -n "$hash" | clip.exe
|
echo -n "$hash" | clip.exe
|
||||||
elif [[ $OS == "Mac" ]];then
|
elif [[ $OS == "Mac" ]];then
|
||||||
echo -n "$hash" | pbcopy
|
echo -n "$hash" | pbcopy
|
||||||
|
elif [ -n "$WAYLAND_DISPLAY" ]; then
|
||||||
|
# Wayland
|
||||||
|
if ! command -v wl-copy &> /dev/null; then
|
||||||
|
echo -e "You need to install 'wl-copy' to send bigger filePathsUnix from cli"
|
||||||
|
echo "Try: sudo apt install wl-clipboard"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Workaround to prevent use of Pipe which has a max letter limit
|
||||||
|
echo -n "$hash" > /tmp/pairdrop-cli-temp/pairdrop_hash_temp
|
||||||
|
wl-copy < /tmp/pairdrop-cli-temp/pairdrop_hash_temp
|
||||||
|
rm /tmp/pairdrop-cli-temp/pairdrop_hash_temp
|
||||||
else
|
else
|
||||||
(echo -n "$hash" | xclip) || echo "You need to install xclip for sending bigger files from cli"
|
# X11
|
||||||
|
if ! command -v xclip &> /dev/null; then
|
||||||
|
echo -e "You need to install 'xclip' to send bigger filePathsUnix from cli"
|
||||||
|
echo "Try: sudo apt install xclip"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -n "$hash" | xclip -sel c
|
||||||
fi
|
fi
|
||||||
hash=
|
hash=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
openPairDrop
|
openPairDrop
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
@ -165,31 +320,32 @@ sendFiles()
|
||||||
# Main program #
|
# Main program #
|
||||||
############################################################
|
############################################################
|
||||||
############################################################
|
############################################################
|
||||||
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||||
|
|
||||||
pushd . > '/dev/null';
|
pushd . > '/dev/null';
|
||||||
SCRIPTPATH="${BASH_SOURCE[0]:-$0}";
|
script_path="${BASH_SOURCE[0]:-$0}";
|
||||||
|
|
||||||
while [ -h "$SCRIPTPATH" ];
|
while [ -h "$script_path" ];
|
||||||
do
|
do
|
||||||
cd "$( dirname -- "$SCRIPTPATH"; )";
|
cd "$( dirname -- "$script_path"; )";
|
||||||
SCRIPTPATH="$( readlink -f -- "$SCRIPTPATH"; )";
|
script_path="$( readlink -f -- "$script_path"; )";
|
||||||
done
|
done
|
||||||
|
|
||||||
cd "$( dirname -- "$SCRIPTPATH"; )" > '/dev/null';
|
cd "$( dirname -- "$script_path"; )" > '/dev/null';
|
||||||
SCRIPTPATH="$( pwd; )";
|
script_path="$( pwd; )";
|
||||||
popd > '/dev/null';
|
popd > '/dev/null';
|
||||||
|
|
||||||
CONFIGPATH="${SCRIPTPATH}/.pairdrop-cli-config"
|
config_path="${script_path}/.pairdrop-cli-config"
|
||||||
|
|
||||||
[ ! -f "$CONFIGPATH" ] &&
|
[ ! -f "$config_path" ] &&
|
||||||
specifyDomain "https://pairdrop.net/" &&
|
specifyDomain "https://pairdrop.net/" &&
|
||||||
[ ! -f "$CONFIGPATH" ] &&
|
[ ! -f "$config_path" ] &&
|
||||||
echo "Could not create config file. Add 'DOMAIN=https://pairdrop.net/' to a file called .pairdrop-cli-config in the same file as this 'pairdrop' bash file"
|
echo "Could not create config file. Add 'DOMAIN=https://pairdrop.net/' to a file called .pairdrop-cli-config in the same file as this 'pairdrop' bash file"
|
||||||
|
|
||||||
[ ! -f "$CONFIGPATH" ] || export "$(grep -v '^#' "$CONFIGPATH" | xargs)"
|
[ ! -f "$config_path" ] || export "$(grep -v '^#' "$config_path" | xargs)"
|
||||||
|
|
||||||
setOs
|
setOs
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# Process the input options. Add options as needed. #
|
# Process the input options. Add options as needed. #
|
||||||
############################################################
|
############################################################
|
||||||
|
@ -198,24 +354,23 @@ setOs
|
||||||
[[ $# -eq 0 ]] && openPairDrop && exit
|
[[ $# -eq 0 ]] && openPairDrop && exit
|
||||||
|
|
||||||
# display help and exit if first argument is "--help" or more than 2 arguments are given
|
# display help and exit if first argument is "--help" or more than 2 arguments are given
|
||||||
[ "$1" == "--help" ] || [[ $# -gt 2 ]] && help && exit
|
[ "$1" == "--help" ] && help && exit
|
||||||
|
|
||||||
while getopts "d:ht:*" option; do
|
while getopts "d:ht:*" option; do
|
||||||
case $option in
|
case $option in
|
||||||
d) # specify domain
|
d) # specify domain - show help and exit if too many arguments
|
||||||
specifyDomain "$2"
|
[[ $# -gt 2 ]] && help && exit
|
||||||
exit;;
|
specifyDomain "$2"
|
||||||
t) # Send text
|
exit;;
|
||||||
sendText
|
t) # Send text - show help and exit if too many arguments
|
||||||
exit;;
|
[[ $# -gt 2 ]] && help && exit
|
||||||
h | ?) # display help and exit
|
sendText
|
||||||
help
|
exit;;
|
||||||
exit;;
|
h | ?) # display help and exit
|
||||||
esac
|
help
|
||||||
|
exit;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Send file(s)
|
# Send file(s)
|
||||||
# display help and exit if 2 arguments are given or if file does not exist
|
sendFiles "$@"
|
||||||
[[ $# -eq 2 ]] || [[ ! -a $1 ]] && help && exit
|
|
||||||
|
|
||||||
sendFiles "$1"
|
|
||||||
|
|
6
pairdrop-cli/pairdrop.sh
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P )
|
||||||
|
|
||||||
|
cd "$parent_path" || exit
|
||||||
|
|
||||||
|
./pairdrop "$@"
|
BIN
pairdrop-cli/send with PairDrop.lnk
Normal file
3
pairdrop-cli/send-with-pairdrop.ps1
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
|
||||||
|
& "$scriptDir\pairdrop.sh" $args
|
20
pairdrop-cli/send-with-pairdrop.sh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# edit this to point to the pairdrop-cli executable
|
||||||
|
pathToPairDropCli="/usr/local/bin/pairdrop-cli/pairdrop"
|
||||||
|
|
||||||
|
# Initialize an array
|
||||||
|
lines=()
|
||||||
|
|
||||||
|
# Read each line into the array
|
||||||
|
while IFS= read -r line; do
|
||||||
|
lines+=("$line")
|
||||||
|
done <<< "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"
|
||||||
|
|
||||||
|
# Get the length of the array
|
||||||
|
length=${#lines[@]}
|
||||||
|
|
||||||
|
# Remove the last entry
|
||||||
|
unset 'lines[length-1]'
|
||||||
|
|
||||||
|
$pathToPairDropCli "${lines[@]}"
|
93
public/fonts/OpenSans/OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
public/fonts/OpenSans/OpenSans-Italic-VariableFont_wdth,wght.ttf
Normal file
BIN
public/fonts/OpenSans/OpenSans-VariableFont_wdth,wght.ttf
Normal file
100
public/fonts/OpenSans/README.txt
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
Open Sans Variable Font
|
||||||
|
=======================
|
||||||
|
|
||||||
|
This download contains Open Sans as both variable fonts and static fonts.
|
||||||
|
|
||||||
|
Open Sans is a variable font with these axes:
|
||||||
|
wdth
|
||||||
|
wght
|
||||||
|
|
||||||
|
This means all the styles are contained in these files:
|
||||||
|
OpenSans-VariableFont_wdth,wght.ttf
|
||||||
|
OpenSans-Italic-VariableFont_wdth,wght.ttf
|
||||||
|
|
||||||
|
If your app fully supports variable fonts, you can now pick intermediate styles
|
||||||
|
that aren’t available as static fonts. Not all apps support variable fonts, and
|
||||||
|
in those cases you can use the static font files for Open Sans:
|
||||||
|
static/OpenSans_Condensed-Light.ttf
|
||||||
|
static/OpenSans_Condensed-Regular.ttf
|
||||||
|
static/OpenSans_Condensed-Medium.ttf
|
||||||
|
static/OpenSans_Condensed-SemiBold.ttf
|
||||||
|
static/OpenSans_Condensed-Bold.ttf
|
||||||
|
static/OpenSans_Condensed-ExtraBold.ttf
|
||||||
|
static/OpenSans_SemiCondensed-Light.ttf
|
||||||
|
static/OpenSans_SemiCondensed-Regular.ttf
|
||||||
|
static/OpenSans_SemiCondensed-Medium.ttf
|
||||||
|
static/OpenSans_SemiCondensed-SemiBold.ttf
|
||||||
|
static/OpenSans_SemiCondensed-Bold.ttf
|
||||||
|
static/OpenSans_SemiCondensed-ExtraBold.ttf
|
||||||
|
static/OpenSans-Light.ttf
|
||||||
|
static/OpenSans-Regular.ttf
|
||||||
|
static/OpenSans-Medium.ttf
|
||||||
|
static/OpenSans-SemiBold.ttf
|
||||||
|
static/OpenSans-Bold.ttf
|
||||||
|
static/OpenSans-ExtraBold.ttf
|
||||||
|
static/OpenSans_Condensed-LightItalic.ttf
|
||||||
|
static/OpenSans_Condensed-Italic.ttf
|
||||||
|
static/OpenSans_Condensed-MediumItalic.ttf
|
||||||
|
static/OpenSans_Condensed-SemiBoldItalic.ttf
|
||||||
|
static/OpenSans_Condensed-BoldItalic.ttf
|
||||||
|
static/OpenSans_Condensed-ExtraBoldItalic.ttf
|
||||||
|
static/OpenSans_SemiCondensed-LightItalic.ttf
|
||||||
|
static/OpenSans_SemiCondensed-Italic.ttf
|
||||||
|
static/OpenSans_SemiCondensed-MediumItalic.ttf
|
||||||
|
static/OpenSans_SemiCondensed-SemiBoldItalic.ttf
|
||||||
|
static/OpenSans_SemiCondensed-BoldItalic.ttf
|
||||||
|
static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf
|
||||||
|
static/OpenSans-LightItalic.ttf
|
||||||
|
static/OpenSans-Italic.ttf
|
||||||
|
static/OpenSans-MediumItalic.ttf
|
||||||
|
static/OpenSans-SemiBoldItalic.ttf
|
||||||
|
static/OpenSans-BoldItalic.ttf
|
||||||
|
static/OpenSans-ExtraBoldItalic.ttf
|
||||||
|
|
||||||
|
Get started
|
||||||
|
-----------
|
||||||
|
|
||||||
|
1. Install the font files you want to use
|
||||||
|
|
||||||
|
2. Use your app's font picker to view the font family and all the
|
||||||
|
available styles
|
||||||
|
|
||||||
|
Learn more about variable fonts
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
|
||||||
|
https://variablefonts.typenetwork.com
|
||||||
|
https://medium.com/variable-fonts
|
||||||
|
|
||||||
|
In desktop apps
|
||||||
|
|
||||||
|
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
|
||||||
|
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
|
||||||
|
|
||||||
|
Online
|
||||||
|
|
||||||
|
https://developers.google.com/fonts/docs/getting_started
|
||||||
|
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
|
||||||
|
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
|
||||||
|
|
||||||
|
Installing fonts
|
||||||
|
|
||||||
|
MacOS: https://support.apple.com/en-us/HT201749
|
||||||
|
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
|
||||||
|
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
|
||||||
|
|
||||||
|
Android Apps
|
||||||
|
|
||||||
|
https://developers.google.com/fonts/docs/android
|
||||||
|
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
Please read the full license text (OFL.txt) to understand the permissions,
|
||||||
|
restrictions and requirements for usage, redistribution, and modification.
|
||||||
|
|
||||||
|
You can use them in your products & projects – print or digital,
|
||||||
|
commercial or otherwise.
|
||||||
|
|
||||||
|
This isn't legal advice, please consider consulting a lawyer and see the full
|
||||||
|
license for all details.
|
BIN
public/fonts/OpenSans/static/OpenSans-Bold.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-BoldItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-ExtraBold.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-ExtraBoldItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-Italic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-Light.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-LightItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-Medium.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-MediumItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-Regular.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-SemiBold.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans-SemiBoldItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-Bold.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-BoldItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-ExtraBold.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-Italic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-Light.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-LightItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-Medium.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-MediumItalic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-Regular.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_Condensed-SemiBold.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_SemiCondensed-Bold.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_SemiCondensed-Italic.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_SemiCondensed-Light.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_SemiCondensed-Medium.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_SemiCondensed-Regular.ttf
Normal file
BIN
public/fonts/OpenSans/static/OpenSans_SemiCondensed-SemiBold.ttf
Normal file
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 269 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 271 KiB |
Before Width: | Height: | Size: 1 MiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 271 KiB |
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 231 KiB |
BIN
public/images/pairdrop_screenshot_mobile_8.png
Normal file
After Width: | Height: | Size: 221 KiB |
|
@ -5,17 +5,17 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<!-- Web App Config -->
|
<!-- Web App Config -->
|
||||||
<title>PairDrop</title>
|
<title>PairDrop | Transfer Files Cross-Platform. No Setup, No Signup.</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="theme-color" content="#3367d6">
|
<meta name="theme-color" content="#3367d6">
|
||||||
<meta name="color-scheme" content="dark light">
|
<meta name="color-scheme" content="dark light">
|
||||||
<meta name="apple-mobile-web-app-capable" content="no">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-title" content="PairDrop">
|
<meta name="apple-mobile-web-app-title" content="PairDrop">
|
||||||
<meta name="application-name" content="PairDrop">
|
<meta name="application-name" content="PairDrop">
|
||||||
<!-- Descriptions -->
|
<!-- Descriptions -->
|
||||||
<meta name="description" content="Instantly share images, videos, PDFs, and links with people nearby. Peer2Peer and Open Source. No Setup, No Signup.">
|
<meta name="description" content="Instantly share images, videos, PDFs, and links with people nearby. Peer2Peer and Open Source. No Setup, No Signup.">
|
||||||
<meta name="keywords" content="File, Transfer, Share, Peer2Peer">
|
<meta name="keywords" content="File, Transfer, Share, Peer2Peer">
|
||||||
<meta name="author" content="RobinLinus">
|
<meta name="author" content="schlagmichdoch">
|
||||||
<meta property="og:title" content="PairDrop">
|
<meta property="og:title" content="PairDrop">
|
||||||
<meta property="og:type" content="article">
|
<meta property="og:type" content="article">
|
||||||
<meta property="og:url" content="https://pairdrop.net/">
|
<meta property="og:url" content="https://pairdrop.net/">
|
||||||
|
@ -28,18 +28,19 @@
|
||||||
<link rel="icon" sizes="96x96" href="images/favicon-96x96.png">
|
<link rel="icon" sizes="96x96" href="images/favicon-96x96.png">
|
||||||
<link rel="shortcut icon" href="images/favicon-96x96.png">
|
<link rel="shortcut icon" href="images/favicon-96x96.png">
|
||||||
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="images/apple-touch-icon.png">
|
||||||
|
<link rel="apple-touch-icon-precomposed" href="images/apple-touch-icon.png">
|
||||||
<meta name="msapplication-TileImage" content="images/mstile-150x150.png">
|
<meta name="msapplication-TileImage" content="images/mstile-150x150.png">
|
||||||
<link rel="fluid-icon" type="image/png" href="images/android-chrome-192x192.png">
|
<link rel="fluid-icon" type="image/png" href="images/android-chrome-192x192.png">
|
||||||
<meta name="twitter:image" content="images/logo_transparent_512x512.png">
|
<meta name="twitter:image" content="images/logo_transparent_512x512.png">
|
||||||
<meta property="og:image" content="images/logo_transparent_512x512.png">
|
<meta property="og:image" content="images/logo_transparent_512x512.png">
|
||||||
<!-- Resources -->
|
<!-- Resources -->
|
||||||
<link rel="preload" href="lang/en.json" as="fetch">
|
<link rel="preload" href="lang/en.json" as="fetch">
|
||||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
<link rel="stylesheet" type="text/css" href="styles/styles-main.css">
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body translate="no">
|
<body translate="no">
|
||||||
<header class="row-reverse opacity-0">
|
<header class="row-reverse wrap opacity-0">
|
||||||
<a href="#about" class="icon-button" data-i18n-key="header.about" data-i18n-attrs="title aria-label">
|
<a href="#about" class="icon-button" data-i18n-key="header.about" data-i18n-attrs="title aria-label">
|
||||||
<svg class="icon">
|
<svg class="icon">
|
||||||
<use xlink:href="#info-outline"></use>
|
<use xlink:href="#info-outline"></use>
|
||||||
|
@ -94,39 +95,79 @@
|
||||||
<use xlink:href="#public-room-icon"></use>
|
<use xlink:href="#public-room-icon"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div id="cancel-paste-mode" class="button" data-i18n-key="header.cancel-paste-mode" data-i18n-attrs="text" hidden></div>
|
<div id="expand" class="icon-button" data-i18n-key="header.expand" data-i18n-attrs="title" hidden>
|
||||||
|
<svg class="icon">
|
||||||
|
<use xlink:href="#caret"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<!-- Center -->
|
<!-- Center -->
|
||||||
<div id="center" class="opacity-0">
|
<div id="center" class="opacity-0">
|
||||||
<!-- Peers -->
|
<!-- Peers -->
|
||||||
<div class="x-peers-filler"></div>
|
<x-peers class="center grow-5"></x-peers>
|
||||||
<x-peers class="center"></x-peers>
|
<x-no-peers class="center grow fade-in no-animation-on-load" data-i18n-key="instructions.no-peers" data-i18n-attrs="data-drop-bg">
|
||||||
<x-no-peers class="no-animation-on-load" data-i18n-key="instructions.no-peers" data-i18n-attrs="data-drop-bg">
|
|
||||||
<h2 data-i18n-key="instructions.no-peers-title" data-i18n-attrs="text"></h2>
|
<h2 data-i18n-key="instructions.no-peers-title" data-i18n-attrs="text"></h2>
|
||||||
<div data-i18n-key="instructions.no-peers-subtitle" data-i18n-attrs="text"></div>
|
<div data-i18n-key="instructions.no-peers-subtitle" data-i18n-attrs="text"></div>
|
||||||
</x-no-peers>
|
</x-no-peers>
|
||||||
<x-instructions data-i18n-key="instructions.x-instructions" data-i18n-attrs="desktop mobile data-drop-peer data-drop-bg">
|
<x-instructions class="grow fade-in" data-i18n-key="instructions.x-instructions" data-i18n-attrs="desktop mobile data-drop-peer data-drop-bg"></x-instructions>
|
||||||
<p id="paste-filename"></p>
|
<div class="shr-panel panel column" hidden>
|
||||||
</x-instructions>
|
<div class="row">
|
||||||
|
<div class="thumb center">
|
||||||
|
<div class="text-thumb row" hidden>
|
||||||
|
<svg>
|
||||||
|
<use xlink:href="#font"></use>
|
||||||
|
</svg>
|
||||||
|
<svg>
|
||||||
|
<use xlink:href="#i-cursor"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="file-thumb" hidden>
|
||||||
|
<svg>
|
||||||
|
<use xlink:href="#file"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="image-thumb" hidden></div>
|
||||||
|
</div>
|
||||||
|
<div class="share-descriptor column p-1">
|
||||||
|
<span class="descriptor-item"></span>
|
||||||
|
<span class="descriptor-other" hidden></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="center btn-row wrap">
|
||||||
|
<div class="cancel-btn btn btn-small btn-rounded btn-dark text-white" data-i18n-key="header.cancel-share-mode" data-i18n-attrs="text"></div>
|
||||||
|
<div class="edit-btn btn btn-small btn-rounded btn-dark text-white" data-i18n-key="header.edit-share-mode" data-i18n-attrs="text" hidden></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="websocket-fallback" hidden>
|
||||||
|
<span data-i18n-key="footer.traffic" data-i18n-attrs="text"></span>
|
||||||
|
<span data-i18n-key="footer.routed" data-i18n-attrs="text"></span>
|
||||||
|
<span data-i18n-key="footer.webrtc" data-i18n-attrs="text"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="column opacity-0">
|
<footer class="column opacity-0">
|
||||||
<svg class="icon logo">
|
<svg class="icon logo">
|
||||||
<use xlink:href="#wifi-tethering"></use>
|
<defs>
|
||||||
|
<linearGradient id="primaryGradient" gradientTransform="rotate(90)">
|
||||||
|
<stop offset="0%" class="start-color" />
|
||||||
|
<stop offset="100%" class="stop-color" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#wifi-tethering" style="fill: url(#primaryGradient);"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="known-as-wrapper">
|
<div class="known-as-wrapper">
|
||||||
<span data-i18n-key="footer.known-as" data-i18n-attrs="text"></span>
|
<span data-i18n-key="footer.known-as" data-i18n-attrs="text"></span>
|
||||||
<div id="display-name" class="badge" data-i18n-key="footer.display-name" data-i18n-attrs="data-placeholder title" placeholder="Loading..." autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable></div>
|
<div id="display-name" class="badge" data-i18n-key="footer.display-name" data-i18n-attrs="data-placeholder title" placeholder="Loading..." autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable></div>
|
||||||
<svg id="edit-pen" class="icon">
|
<svg class="icon edit-pen">
|
||||||
<use xlink:href="#edit-pen-icon"></use>
|
<use xlink:href="#edit-pen-icon"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="discovery-wrapper row">
|
<div class="discovery-wrapper panel border row">
|
||||||
<div class="row center">
|
<div class="row center">
|
||||||
<span data-i18n-key="footer.discovery" data-i18n-attrs="text"></span>
|
<span data-i18n-key="footer.discovery" data-i18n-attrs="text"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center wrap">
|
||||||
<span class="badge badge-room-ip" data-i18n-key="footer.on-this-network" data-i18n-attrs="text title"></span>
|
<span class="badge badge-room-ip" data-i18n-key="footer.on-this-network" data-i18n-attrs="text title"></span>
|
||||||
<span class="badge badge-room-secret pointer" data-i18n-key="footer.paired-devices" data-i18n-attrs="text title" hidden></span>
|
<span class="badge badge-room-secret pointer" data-i18n-key="footer.paired-devices" data-i18n-attrs="text title" hidden></span>
|
||||||
<span class="badge badge-room-public-id pointer" data-i18n-key="footer.public-room-devices" data-i18n-attrs="title" hidden>in room IAIAI</span>
|
<span class="badge badge-room-public-id pointer" data-i18n-key="footer.public-room-devices" data-i18n-attrs="title" hidden>in room IAIAI</span>
|
||||||
|
@ -138,82 +179,89 @@
|
||||||
<x-dialog id="language-select-dialog">
|
<x-dialog id="language-select-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<h2 class="center" data-i18n-key="dialogs.language-selector-title" data-i18n-attrs="text"></h2>
|
<h2 class="dialog-title" data-i18n-key="dialogs.language-selector-title" data-i18n-attrs="text"></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="language-buttons">
|
<div class="language-buttons p2">
|
||||||
<button class="button fw" data-i18n-key="dialogs.system-language" data-i18n-attrs="text"></button>
|
<button class="btn fw wrap">
|
||||||
<button class="button fw" value="ar">
|
<span data-i18n-key="dialogs.system-language" data-i18n-attrs="text"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn fw wrap" value="ar">
|
||||||
<span>العربية</span>
|
<span>العربية</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Arabic)</span>
|
<span>(Arabic)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="de">
|
<button class="btn fw wrap" value="de">
|
||||||
<span>Deutsch</span>
|
<span>Deutsch</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(German)</span>
|
<span>(German)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="en">
|
<button class="btn fw wrap" value="en">
|
||||||
<span>English</span>
|
<span>English</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="es">
|
<button class="btn fw wrap" value="es">
|
||||||
<span>Español</span>
|
<span>Español</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Spanish)</span>
|
<span>(Spanish)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="fr">
|
<button class="btn fw wrap" value="fr">
|
||||||
<span>Français</span>
|
<span>Français</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(French)</span>
|
<span>(French)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="id">
|
<button class="btn fw wrap" value="id">
|
||||||
<span>Bahasa Indonesia</span>
|
<span>Bahasa Indonesia</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Indonesian)</span>
|
<span>(Indonesian)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="it">
|
<button class="btn fw wrap" value="it">
|
||||||
<span>Italiano</span>
|
<span>Italiano</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Italian)</span>
|
<span>(Italian)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="nl">
|
<button class="btn fw wrap" value="nl">
|
||||||
<span>Nederlands</span>
|
<span>Nederlands</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Dutch)</span>
|
<span>(Dutch)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="nb">
|
<button class="btn fw wrap" value="nb">
|
||||||
<span>Norsk</span>
|
<span>Norsk</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Norwegian)</span>
|
<span>(Norwegian)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="ro">
|
<button class="btn fw wrap" value="pt-BR">
|
||||||
|
<span>Português do Brasil</span>
|
||||||
|
<span> - </span>
|
||||||
|
<span>(Brazilian Portuguese)</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn fw wrap" value="ro">
|
||||||
<span>Română</span>
|
<span>Română</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Romanian)</span>
|
<span>(Romanian)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="ru">
|
<button class="btn fw wrap" value="ru">
|
||||||
<span>Русский язык</span>
|
<span>Русский язык</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Russian)</span>
|
<span>(Russian)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="tr">
|
<button class="btn fw wrap" value="tr">
|
||||||
<span>Türkçe</span>
|
<span>Türkçe</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Turkish)</span>
|
<span>(Turkish)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="zh-CN">
|
<button class="btn fw wrap" value="zh-CN">
|
||||||
<span>中文</span>
|
<span>中文</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Chinese)</span>
|
<span>(Chinese)</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="button fw" value="ja">
|
<button class="btn fw wrap" value="ja">
|
||||||
<span>日本語</span>
|
<span>日本語</span>
|
||||||
<span>-</span>
|
<span> - </span>
|
||||||
<span>(Japanese)</span>
|
<span>(Japanese)</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="center row-reverse button-row">
|
<div class="center row-reverse btn-row wrap">
|
||||||
<button class="button" type="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
<button class="btn btn-rounded btn-grey" type="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -223,12 +271,12 @@
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center text-center">
|
<x-background class="full center text-center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<h2 class="center" data-i18n-key="dialogs.pair-devices-title" data-i18n-attrs="text"></h2>
|
<h2 class="dialog-title" data-i18n-key="dialogs.pair-devices-title" data-i18n-attrs="text"></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="center key-qr-code" data-i18n-key="dialogs.pair-devices-qr-code" data-i18n-attrs="title"></div>
|
<div class="center key-qr-code pointer" data-i18n-key="dialogs.pair-devices-qr-code" data-i18n-attrs="title"></div>
|
||||||
<h1 class="center key" dir="ltr">000 000</h1>
|
<h1 class="center key" dir="ltr">000 000</h1>
|
||||||
<p class="center text-center key-instructions">
|
<p class="center text-center key-instructions">
|
||||||
<span class="font-subheading" data-i18n-key="dialogs.input-key-on-this-device" data-i18n-attrs="text"></span>
|
<span class="font-subheading" data-i18n-key="dialogs.input-key-on-this-device" data-i18n-attrs="text"></span>
|
||||||
|
@ -242,8 +290,8 @@
|
||||||
<span data-i18n-key="dialogs.hr-or" data-i18n-attrs="text"></span>
|
<span data-i18n-key="dialogs.hr-or" data-i18n-attrs="text"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<div class="column fw">
|
||||||
<div class="input-key-container six-chars" dir="ltr">
|
<div class="input-key-container six-chars" dir="ltr">
|
||||||
<input type="tel" class="textarea center" aria-label="pair-key-char-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder disabled>
|
<input type="tel" class="textarea center" aria-label="pair-key-char-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder disabled>
|
||||||
<input type="tel" class="textarea center" aria-label="pair-key-char-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder disabled>
|
<input type="tel" class="textarea center" aria-label="pair-key-char-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder disabled>
|
||||||
|
@ -255,9 +303,9 @@
|
||||||
<p class="font-subheading center text-center" data-i18n-key="dialogs.enter-key-from-another-device" data-i18n-attrs="text"></p>
|
<p class="font-subheading center text-center" data-i18n-key="dialogs.enter-key-from-another-device" data-i18n-attrs="text"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-row row-reverse">
|
<div class="btn-row row-reverse wrap">
|
||||||
<button class="button" type="submit" data-i18n-key="dialogs.pair" data-i18n-attrs="text" disabled></button>
|
<button class="btn btn-rounded btn-grey" type="submit" data-i18n-key="dialogs.pair" data-i18n-attrs="text" disabled></button>
|
||||||
<button class="button" type="button" data-i18n-key="dialogs.cancel" data-i18n-attrs="text" close></button>
|
<button class="btn btn-rounded btn-grey" type="button" data-i18n-key="dialogs.cancel" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -268,8 +316,8 @@
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center text-center">
|
<x-background class="full center text-center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<h2 class="center" data-i18n-key="dialogs.edit-paired-devices-title" data-i18n-attrs="text"></h2>
|
<h2 class="dialog-title" data-i18n-key="dialogs.edit-paired-devices-title" data-i18n-attrs="text"></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="paired-devices-wrapper" data-i18n-key="dialogs.paired-devices-wrapper" data-i18n-attrs="data-empty"></div>
|
<div class="paired-devices-wrapper" data-i18n-key="dialogs.paired-devices-wrapper" data-i18n-attrs="data-empty"></div>
|
||||||
<div class="font-subheading center">
|
<div class="font-subheading center">
|
||||||
|
@ -279,8 +327,8 @@
|
||||||
<span data-i18n-key="dialogs.auto-accept-instructions-2" data-i18n-attrs="text"></span>
|
<span data-i18n-key="dialogs.auto-accept-instructions-2" data-i18n-attrs="text"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="center row-reverse button-row">
|
<div class="center row-reverse btn-row wrap">
|
||||||
<button class="button" type="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
<button class="btn btn-rounded btn-grey" type="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -291,14 +339,12 @@
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center text-center">
|
<x-background class="full center text-center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<h2 class="dialog-title" data-i18n-key="dialogs.temporary-public-room-title" data-i18n-attrs="text"></h2>
|
||||||
<h2 class="center" data-i18n-key="dialogs.temporary-public-room-title" data-i18n-attrs="text"></h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="center key-qr-code" data-i18n-key="dialogs.public-room-qr-code" data-i18n-attrs="title"></div>
|
<div class="center key-qr-code pointer" data-i18n-key="dialogs.public-room-qr-code" data-i18n-attrs="title"></div>
|
||||||
<h1 class="center key" dir="ltr"></h1>
|
<h1 class="center key" dir="ltr"></h1>
|
||||||
<p class="center text-center key-instructions">
|
<p class="center text-center key-instructions">
|
||||||
<span class="font-subheading" data-i18n-key="dialogs.input-room-id-on-another-device" data-i18n-attrs="text"></span>
|
<span class="font-subheading" data-i18n-key="dialogs.input-room-id-on-another-device" data-i18n-attrs="text"></span>
|
||||||
|
@ -312,8 +358,8 @@
|
||||||
<span data-i18n-key="dialogs.hr-or" data-i18n-attrs="text"></span>
|
<span data-i18n-key="dialogs.hr-or" data-i18n-attrs="text"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<div class="column fw">
|
||||||
<div class="input-key-container" dir="ltr">
|
<div class="input-key-container" dir="ltr">
|
||||||
<input type="text" class="textarea center" aria-label="room-id-char-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder disabled>
|
<input type="text" class="textarea center" aria-label="room-id-char-1" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" autofocus contenteditable placeholder disabled>
|
||||||
<input type="text" class="textarea center" aria-label="room-id-char-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder disabled>
|
<input type="text" class="textarea center" aria-label="room-id-char-2" maxlength="1" autocorrect="off" autocomplete="off" autocapitalize="none" spellcheck="false" contenteditable placeholder disabled>
|
||||||
|
@ -324,10 +370,12 @@
|
||||||
<p class="font-subheading center text-center" data-i18n-key="dialogs.enter-room-id-from-another-device" data-i18n-attrs="text"></p>
|
<p class="font-subheading center text-center" data-i18n-key="dialogs.enter-room-id-from-another-device" data-i18n-attrs="text"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center row-reverse button-row">
|
<div class="center row-reverse btn-row wrap">
|
||||||
<button class="button" type="submit" data-i18n-key="dialogs.join" data-i18n-attrs="text" disabled></button>
|
<div class="row-reverse wrap grow-2">
|
||||||
<button class="button" type="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
<button class="btn btn-rounded btn-grey" type="submit" data-i18n-key="dialogs.join" data-i18n-attrs="text" disabled></button>
|
||||||
<button class="button leave-room" type="button" data-i18n-key="dialogs.leave" data-i18n-attrs="text"></button>
|
<button class="btn btn-rounded btn-grey leave-room" type="button" data-i18n-key="dialogs.leave" data-i18n-attrs="text"></button>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-rounded btn-grey" type="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -337,12 +385,10 @@
|
||||||
<x-dialog id="receive-request-dialog">
|
<x-dialog id="receive-request-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<h2 class="dialog-title"></h2>
|
||||||
<h2 class="center"></h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column center file-description">
|
<div class="column center file-description">
|
||||||
<div>
|
<div>
|
||||||
<span class="display-name badge"></span>
|
<span class="display-name badge"></span>
|
||||||
|
@ -358,9 +404,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center file-preview"></div>
|
<div class="center file-preview"></div>
|
||||||
<div class="row-reverse center button-row">
|
<div class="row-reverse center btn-row wrap">
|
||||||
<button id="accept-request" class="button" title="ENTER" data-i18n-key="dialogs.accept" data-i18n-attrs="text" autofocus></button>
|
<button id="accept-request" class="btn btn-rounded btn-grey" title="ENTER" data-i18n-key="dialogs.accept" data-i18n-attrs="text" autofocus disabled></button>
|
||||||
<button id="decline-request" class="button" title="ESCAPE" data-i18n-key="dialogs.decline" data-i18n-attrs="text"></button>
|
<button id="decline-request" class="btn btn-rounded btn-grey" title="ESCAPE" data-i18n-key="dialogs.decline" data-i18n-attrs="text"></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -369,12 +415,10 @@
|
||||||
<x-dialog id="receive-file-dialog">
|
<x-dialog id="receive-file-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<h2 class="dialog-title"></h2>
|
||||||
<h2 class="center"></h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column center file-description">
|
<div class="column center file-description">
|
||||||
<div>
|
<div>
|
||||||
<span class="display-name badge"></span>
|
<span class="display-name badge"></span>
|
||||||
|
@ -390,10 +434,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="center file-preview"></div>
|
<div class="center file-preview"></div>
|
||||||
<div class="row-reverse center button-row">
|
<div class="row-reverse center btn-row wrap">
|
||||||
<button id="share-btn" class="button" data-i18n-key="dialogs.share" data-i18n-attrs="text" hidden></button>
|
<button id="share-btn" class="btn btn-rounded btn-grey" data-i18n-key="dialogs.share" data-i18n-attrs="text" hidden></button>
|
||||||
<button id="download-btn" class="button" data-i18n-key="dialogs.download" data-i18n-attrs="text" autofocus></button>
|
<button id="download-btn" class="btn btn-rounded btn-grey" data-i18n-key="dialogs.download" data-i18n-attrs="text" autofocus disabled></button>
|
||||||
<button class="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
<button class="btn btn-rounded btn-grey" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -403,12 +447,10 @@
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column">
|
<h2 class="dialog-title" data-i18n-key="dialogs.send-message-title" data-i18n-attrs="text"></h2>
|
||||||
<h2 class="center" data-i18n-key="dialogs.send-message-title" data-i18n-attrs="text"></h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row center display-name-wrapper">
|
<div class="row center p-2 display-name-wrapper">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<span data-i18n-key="dialogs.send-message-to" data-i18n-attrs="text"></span>
|
<span data-i18n-key="dialogs.send-message-to" data-i18n-attrs="text"></span>
|
||||||
|
@ -416,14 +458,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row p-2">
|
||||||
<div class="column fw">
|
<div class="column fw">
|
||||||
<div id="text-input" class="textarea" role="textbox" data-i18n-key="dialogs.message" data-i18n-attrs="title" autocapitalize="none" spellcheck="false" autofocus contenteditable></div>
|
<div class="fw textarea" role="textbox" data-i18n-key="dialogs.message" data-i18n-attrs="title placeholder" autofocus contenteditable></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-row row-reverse">
|
<div class="btn-row row-reverse wrap">
|
||||||
<button class="button" type="submit" title="CTRL/⌘ + ENTER" data-i18n-key="dialogs.send" data-i18n-attrs="text" disabled></button>
|
<button class="btn btn-rounded btn-grey" type="submit" title="CTRL/⌘ + ENTER" data-i18n-key="dialogs.send" data-i18n-attrs="text" disabled></button>
|
||||||
<button class="button" type="button" title="ESCAPE" data-i18n-key="dialogs.cancel" data-i18n-attrs="text" close></button>
|
<button class="btn btn-rounded btn-grey" type="button" title="ESCAPE" data-i18n-key="dialogs.cancel" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -433,23 +475,55 @@
|
||||||
<x-dialog id="receive-text-dialog">
|
<x-dialog id="receive-text-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<h2 class="text-center" data-i18n-key="dialogs.receive-text-title" data-i18n-attrs="text"></h2>
|
<h2 class="dialog-title" class="text-center" data-i18n-key="dialogs.receive-text-title" data-i18n-attrs="text"></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2 display-name-wrapper">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<span class="display-name badge"></span>
|
<span class="display-name badge"></span>
|
||||||
<span data-i18n-key="dialogs.has-sent" data-i18n-attrs="text"></span>
|
<span data-i18n-key="dialogs.has-sent" data-i18n-attrs="text"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row center">
|
<div class="row center p-2">
|
||||||
<div class="column fw">
|
<div class="column fw">
|
||||||
<div id="text" class="textarea fw"></div>
|
<div id="text" class="textarea"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-reverse center button-row">
|
<div class="row-reverse center btn-row wrap">
|
||||||
<button id="copy" class="button" title="CTRL/⌘ + C" data-i18n-key="dialogs.copy" data-i18n-attrs="text"></button>
|
<button id="copy" class="btn btn-rounded btn-grey" title="CTRL/⌘ + C" data-i18n-key="dialogs.copy" data-i18n-attrs="text"></button>
|
||||||
<button id="close" class="button" title="ESCAPE" data-i18n-key="dialogs.close" data-i18n-attrs="text"></button>
|
<button id="close" class="btn btn-rounded btn-grey" title="ESCAPE" data-i18n-key="dialogs.close" data-i18n-attrs="text"></button>
|
||||||
|
</div>
|
||||||
|
</x-paper>
|
||||||
|
</x-background>
|
||||||
|
</x-dialog>
|
||||||
|
<!-- Share Text Dialog -->
|
||||||
|
<x-dialog id="share-text-dialog">
|
||||||
|
<x-background class="full center">
|
||||||
|
<x-paper shadow="2">
|
||||||
|
<div class="row center p-2">
|
||||||
|
<h2 class="dialog-title" data-i18n-key="dialogs.share-text-title" data-i18n-attrs="text"></h2>
|
||||||
|
</div>
|
||||||
|
<div class="row center p-2 pb-0">
|
||||||
|
<div class="column">
|
||||||
|
<div class="text-center">
|
||||||
|
<span data-i18n-key="dialogs.share-text-subtitle" data-i18n-attrs="text"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row p-2">
|
||||||
|
<div class="column fw">
|
||||||
|
<div class="fw textarea" role="textbox" data-i18n-key="dialogs.message" data-i18n-attrs="title placeholder" contenteditable></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row p-2 center">
|
||||||
|
<span class="mx-1" data-i18n-key="dialogs.share-text-checkbox" data-i18n-attrs="text"></span>
|
||||||
|
<label class="pointer switch mx-1">
|
||||||
|
<input type="checkbox">
|
||||||
|
<div class="slider round"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="btn-row row-reverse wrap">
|
||||||
|
<button class="btn btn-rounded btn-grey" type="submit" title="CTRL/⌘ + ENTER" data-i18n-key="dialogs.approve" data-i18n-attrs="text" autofocus disabled></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
|
@ -458,17 +532,29 @@
|
||||||
<x-dialog id="base64-paste-dialog">
|
<x-dialog id="base64-paste-dialog">
|
||||||
<x-background class="full center">
|
<x-background class="full center">
|
||||||
<x-paper shadow="2">
|
<x-paper shadow="2">
|
||||||
<button class="button center" id="base64-paste-btn" title="Paste"></button>
|
<div class="row center p-2">
|
||||||
<div class="textarea" placeholder="Paste here to send files" title="CMD/⌘ + V" contenteditable hidden></div>
|
<h2 class="dialog-title"></h2>
|
||||||
<div class="row-reverse center button-row">
|
</div>
|
||||||
<button class="button" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
<div class="row p-2">
|
||||||
|
<button class="btn btn-rounded btn-grey center" id="base64-paste-btn" title="Paste"></button>
|
||||||
|
<div class="textarea" title="CMD/⌘ + V" contenteditable hidden></div>
|
||||||
|
</div>
|
||||||
|
<div class="row-reverse center btn-row wrap">
|
||||||
|
<button class="btn btn-rounded btn-grey" data-i18n-key="dialogs.close" data-i18n-attrs="text" close></button>
|
||||||
</div>
|
</div>
|
||||||
</x-paper>
|
</x-paper>
|
||||||
</x-background>
|
</x-background>
|
||||||
</x-dialog>
|
</x-dialog>
|
||||||
<!-- Toast -->
|
<!-- Toast -->
|
||||||
<div class="toast-container full center">
|
<div class="toast-container full center">
|
||||||
<x-toast id="toast" class="row center" shadow="1"></x-toast>
|
<x-toast id="toast" shadow="1">
|
||||||
|
<span class="center text-center"></span>
|
||||||
|
<div class="icon-button" data-i18n-key="dialogs.close-toast" data-i18n-attrs="title">
|
||||||
|
<svg class="icon">
|
||||||
|
<use xlink:href="#close-icon"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</x-toast>
|
||||||
</div>
|
</div>
|
||||||
<!-- About Page -->
|
<!-- About Page -->
|
||||||
<x-about id="about" class="full center column">
|
<x-about id="about" class="full center column">
|
||||||
|
@ -543,9 +629,9 @@
|
||||||
<symbol id="github">
|
<symbol id="github">
|
||||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path>
|
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
<g id="notifications">
|
<symbol id="notifications">
|
||||||
<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"></path>
|
<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"></path>
|
||||||
</g>
|
</symbol>
|
||||||
<symbol id="homescreen">
|
<symbol id="homescreen">
|
||||||
<path fill="none" d="M0 0h24v24H0V0z"></path>
|
<path fill="none" d="M0 0h24v24H0V0z"></path>
|
||||||
<path d="M18 1.01L8 1c-1.1 0-2 .9-2 2v3h2V5h10v14H8v-1H6v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM10 15h2V8H5v2h3.59L3 15.59 4.41 17 10 11.41z"></path>
|
<path d="M18 1.01L8 1c-1.1 0-2 .9-2 2v3h2V5h10v14H8v-1H6v3c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM10 15h2V8H5v2h3.59L3 15.59 4.41 17 10 11.41z"></path>
|
||||||
|
@ -555,11 +641,13 @@
|
||||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"></path>
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol id="icon-theme-auto" viewBox="0 0 24 24">
|
<symbol id="icon-theme-auto" viewBox="-54 -54 620 620">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-54 -54 620 620"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M448 256c0-106-86-192-192-192V448c106 0 192-86 192-192zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"></path></svg>
|
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||||
|
<path d="M448 256c0-106-86-192-192-192V448c106 0 192-86 192-192zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol id="icon-theme-light" viewBox="0 0 24 24">
|
<symbol id="icon-theme-light" viewBox="-54 -54 620 620">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-54 -54 620 620"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"></path></svg>
|
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||||
|
<path d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
<symbol id="icon-theme-dark" viewBox="0 0 24 24">
|
<symbol id="icon-theme-dark" viewBox="0 0 24 24">
|
||||||
<rect fill="none" height="24" width="24"></rect><path d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path>
|
<rect fill="none" height="24" width="24"></rect><path d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path>
|
||||||
|
@ -586,17 +674,28 @@
|
||||||
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
<!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||||
<path d="M0 128C0 92.7 28.7 64 64 64H256h48 16H576c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H320 304 256 64c-35.3 0-64-28.7-64-64V128zm320 0V384H576V128H320zM178.3 175.9c-3.2-7.2-10.4-11.9-18.3-11.9s-15.1 4.7-18.3 11.9l-64 144c-4.5 10.1 .1 21.9 10.2 26.4s21.9-.1 26.4-10.2l8.9-20.1h73.6l8.9 20.1c4.5 10.1 16.3 14.6 26.4 10.2s14.6-16.3 10.2-26.4l-64-144zM160 233.2L179 276H141l19-42.8zM448 164c11 0 20 9 20 20v4h44 16c11 0 20 9 20 20s-9 20-20 20h-2l-1.6 4.5c-8.9 24.4-22.4 46.6-39.6 65.4c.9 .6 1.8 1.1 2.7 1.6l18.9 11.3c9.5 5.7 12.5 18 6.9 27.4s-18 12.5-27.4 6.9l-18.9-11.3c-4.5-2.7-8.8-5.5-13.1-8.5c-10.6 7.5-21.9 14-34 19.4l-3.6 1.6c-10.1 4.5-21.9-.1-26.4-10.2s.1-21.9 10.2-26.4l3.6-1.6c6.4-2.9 12.6-6.1 18.5-9.8l-12.2-12.2c-7.8-7.8-7.8-20.5 0-28.3s20.5-7.8 28.3 0l14.6 14.6 .5 .5c12.4-13.1 22.5-28.3 29.8-45H448 376c-11 0-20-9-20-20s9-20 20-20h52v-4c0-11 9-20 20-20z"></path>
|
<path d="M0 128C0 92.7 28.7 64 64 64H256h48 16H576c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H320 304 256 64c-35.3 0-64-28.7-64-64V128zm320 0V384H576V128H320zM178.3 175.9c-3.2-7.2-10.4-11.9-18.3-11.9s-15.1 4.7-18.3 11.9l-64 144c-4.5 10.1 .1 21.9 10.2 26.4s21.9-.1 26.4-10.2l8.9-20.1h73.6l8.9 20.1c4.5 10.1 16.3 14.6 26.4 10.2s14.6-16.3 10.2-26.4l-64-144zM160 233.2L179 276H141l19-42.8zM448 164c11 0 20 9 20 20v4h44 16c11 0 20 9 20 20s-9 20-20 20h-2l-1.6 4.5c-8.9 24.4-22.4 46.6-39.6 65.4c.9 .6 1.8 1.1 2.7 1.6l18.9 11.3c9.5 5.7 12.5 18 6.9 27.4s-18 12.5-27.4 6.9l-18.9-11.3c-4.5-2.7-8.8-5.5-13.1-8.5c-10.6 7.5-21.9 14-34 19.4l-3.6 1.6c-10.1 4.5-21.9-.1-26.4-10.2s.1-21.9 10.2-26.4l3.6-1.6c6.4-2.9 12.6-6.1 18.5-9.8l-12.2-12.2c-7.8-7.8-7.8-20.5 0-28.3s20.5-7.8 28.3 0l14.6 14.6 .5 .5c12.4-13.1 22.5-28.3 29.8-45H448 376c-11 0-20-9-20-20s9-20 20-20h52v-4c0-11 9-20 20-20z"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
<symbol id="i-cursor" viewBox="-180 0 640 512">
|
||||||
|
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.-->
|
||||||
|
<path d="M.1 29.3C-1.4 47 11.7 62.4 29.3 63.9l8 .7C70.5 67.3 96 95 96 128.3V224H64c-17.7 0-32 14.3-32 32s14.3 32 32 32H96v95.7c0 33.3-25.5 61-58.7 63.8l-8 .7C11.7 449.6-1.4 465 .1 482.7s16.9 30.7 34.5 29.2l8-.7c34.1-2.8 64.2-18.9 85.4-42.9c21.2 24 51.2 40.1 85.4 42.9l8 .7c17.6 1.5 33.1-11.6 34.5-29.2s-11.6-33.1-29.2-34.5l-8-.7C185.5 444.7 160 417 160 383.7V288h32c17.7 0 32-14.3 32-32s-14.3-32-32-32H160V128.3c0-33.3 25.5-61 58.7-63.8l8-.7c17.6-1.5 30.7-16.9 29.2-34.5S239-1.4 221.3 .1l-8 .7C179.2 3.6 149.2 19.7 128 43.7c-21.2-24-51.2-40-85.4-42.9l-8-.7C17-1.4 1.6 11.7 .1 29.3z"></path>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="font" viewBox="-100 0 640 512">
|
||||||
|
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.-->
|
||||||
|
<path d="M254 52.8C249.3 40.3 237.3 32 224 32s-25.3 8.3-30 20.8L57.8 416H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32h-1.8l18-48H303.8l18 48H320c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H390.2L254 52.8zM279.8 304H168.2L224 155.1 279.8 304z"></path>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="file" viewBox="-130 0 650 530">
|
||||||
|
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.-->
|
||||||
|
<path d="M320 464c8.8 0 16-7.2 16-16V160H256c-17.7 0-32-14.3-32-32V48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320zM0 64C0 28.7 28.7 0 64 0H229.5c17 0 33.3 6.7 45.3 18.7l90.5 90.5c12 12 18.7 28.3 18.7 45.3V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64z"></path>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="caret" viewBox="0 0 320 512">
|
||||||
|
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M137.4 374.6c12.5 12.5 32.8 12.5 45.3 0l128-128c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8L32 192c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l128 128z"></path>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="scripts/localization.js"></script>
|
<script src="scripts/localization.js"></script>
|
||||||
<script src="scripts/theme.js"></script>
|
<script src="scripts/persistent-storage.js"></script>
|
||||||
<script src="scripts/network.js"></script>
|
<script src="scripts/ui-main.js"></script>
|
||||||
<script src="scripts/ui.js"></script>
|
<script src="scripts/main.js"></script>
|
||||||
<script src="scripts/util.js"></script>
|
|
||||||
<script src="scripts/QRCode.min.js" async></script>
|
|
||||||
<script src="scripts/zip.min.js" async></script>
|
|
||||||
<script src="scripts/NoSleep.min.js" async></script>
|
|
||||||
<!-- Sounds -->
|
<!-- Sounds -->
|
||||||
<audio id="blop" autobuffer="true">
|
<audio id="blop" autobuffer="true">
|
||||||
<source src="sounds/blop.mp3" type="audio/mpeg">
|
<source src="sounds/blop.mp3" type="audio/mpeg">
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"paired-devices": "بواسطة الأجهزة المقترنة",
|
"paired-devices": "بواسطة الأجهزة المقترنة",
|
||||||
"on-this-network": "على هذه الشبكة",
|
"on-this-network": "على هذه الشبكة",
|
||||||
"routed": "توجيهّا من خلال الخادم",
|
"routed": "توجيهّا من خلال الخادم",
|
||||||
"discovery": "يمكنك اكتشاف:",
|
"discovery": "يمكنك اكتشافك:",
|
||||||
"on-this-network_title": "يمكن للجميع اكتشافك على هذه الشبكة.",
|
"on-this-network_title": "يمكن للجميع اكتشافك على هذه الشبكة.",
|
||||||
"known-as": "أنت معروف بأنك:"
|
"known-as": "أنت معروف بأنك:"
|
||||||
},
|
},
|
||||||
|
@ -20,71 +20,71 @@
|
||||||
"message-received": "تم استلام الرابط بواسطة {{name}} - انقر للفتح",
|
"message-received": "تم استلام الرابط بواسطة {{name}} - انقر للفتح",
|
||||||
"rate-limit-join-key": "تم الوصول إلى الحد الأقصى. انتظر 10 ثوان وحاول مرة أخرى.",
|
"rate-limit-join-key": "تم الوصول إلى الحد الأقصى. انتظر 10 ثوان وحاول مرة أخرى.",
|
||||||
"connecting": "يتصل …",
|
"connecting": "يتصل …",
|
||||||
"pairing-key-invalidated": "المفتاح {{key}} خاطئ.",
|
"pairing-key-invalidated": "المفتاح {{key}} خاطئ",
|
||||||
"pairing-key-invalid": "مُفتاح خاطئ",
|
"pairing-key-invalid": "مُفتاح خاطئ",
|
||||||
"connected": "متصل.",
|
"connected": "متصل",
|
||||||
"pairing-not-persistent": "الأجهزة المقترنة ليست ثابتة.",
|
"pairing-not-persistent": "الأجهزة المقترنة ليست ثابتة",
|
||||||
"text-content-incorrect": "محتوى النص غير صحيح.",
|
"text-content-incorrect": "محتوى النص غير صحيح",
|
||||||
"message-transfer-completed": "اكتمل نقل الرسالة.",
|
"message-transfer-completed": "اكتمل نقل الرسالة",
|
||||||
"file-transfer-completed": "اكتمل نقل الملف.",
|
"file-transfer-completed": "اكتمل نقل الملف",
|
||||||
"file-content-incorrect": "محتوى الملف غير صحيح.",
|
"file-content-incorrect": "محتوى الملف غير صحيح",
|
||||||
"files-incorrect": "الملفات غير صحيحة.",
|
"files-incorrect": "الملفات غير صحيحة",
|
||||||
"selected-peer-left": "مُحَدد الاجهزة المقترنة.",
|
"selected-peer-left": "مُحَدد الاجهزة المقترنة",
|
||||||
"link-received": "تم استلام الرابط بواسطة {{name}} - انقر للفتح",
|
"link-received": "تم استلام الرابط بواسطة {{name}} - انقر للفتح",
|
||||||
"online": "لقد عدت متصلاً بالإنترنت",
|
"online": "لقد عدت متصلاً بالإنترنت",
|
||||||
"public-room-left": "الخروج من الغرفة العامة {{publicRoomId}}",
|
"public-room-left": "الخروج من الغرفة العامة {{publicRoomId}}",
|
||||||
"copied-text": "نُسِخَ النص إلى الحافظة",
|
"copied-text": "نُسِخَ النص إلى الحافظة",
|
||||||
"display-name-random-again": "يتم إنشاء اسم العرض بشكل عشوائي مرة أخرى.",
|
"display-name-random-again": "يتم إنشاء اسم العرض بشكل عشوائي مرة أخرى",
|
||||||
"display-name-changed-permanently": "يتم تغيير اسم العرض بشكل دائم.",
|
"display-name-changed-permanently": "يتم تغيير اسم العرض بشكل دائم",
|
||||||
"copied-to-clipboard-error": "النسخ غير ممكن. انسخ يدويًا.",
|
"copied-to-clipboard-error": "النسخ غير ممكن. انسخ يدويًا.",
|
||||||
"pairing-success": "الأجهزة المقترنة.",
|
"pairing-success": "الأجهزة المقترنة",
|
||||||
"clipboard-content-incorrect": "محتوى الحافظة غير صحيح.",
|
"clipboard-content-incorrect": "محتوى الحافظة غير صحيح",
|
||||||
"display-name-changed-temporarily": "تم تغيير اسم العرض لهذه الجلسة فقط.",
|
"display-name-changed-temporarily": "تم تغيير اسم العرض لهذه الجلسة فقط",
|
||||||
"copied-to-clipboard": "تم النسخ إلى الحافظة",
|
"copied-to-clipboard": "تم النسخ إلى الحافظة",
|
||||||
"offline": "انت غير متصل",
|
"offline": "انت غير متصل",
|
||||||
"pairing-tabs-error": "من المستحيل إقران علامتي تبويب متصفح الويب.",
|
"pairing-tabs-error": "من المستحيل إقران علامتي تبويب متصفح الويب",
|
||||||
"public-room-id-invalid": "معرف الغرفة غير صالح",
|
"public-room-id-invalid": "معرف الغرفة غير صالح",
|
||||||
"click-to-download": "إضغط للتحميل",
|
"click-to-download": "إضغط للتحميل",
|
||||||
"pairing-cleared": "جميع الأجهزة غير مقترنة.",
|
"pairing-cleared": "جميع الأجهزة غير مقترنة",
|
||||||
"notifications-enabled": "تم تمكين الإشعارات.",
|
"notifications-enabled": "تم تمكين الإشعارات",
|
||||||
"online-requirement-pairing": "يجب أن تكون متصلاً بالإنترنت لإقران الأجهزة.",
|
"online-requirement-pairing": "يجب أن تكون متصلاً بالإنترنت لإقران الأجهزة",
|
||||||
"ios-memory-limit": "لا يمكن إرسال ملفات إلى iOS إلا بحجم يصل إلى 200 ميجابايت مرة واحدة",
|
"ios-memory-limit": "لا يمكن إرسال ملفات إلى iOS إلا بحجم يصل إلى 200 ميجابايت مرة واحدة",
|
||||||
"online-requirement-public-room": "يجب أن تكون متصلاً بالإنترنت لإنشاء غرفة عامة.",
|
"online-requirement-public-room": "يجب أن تكون متصلاً بالإنترنت لإنشاء غرفة عامة",
|
||||||
"copied-text-error": "فشلت الكتابة من الحافظة. انسخ يدويًا!",
|
"copied-text-error": "فشلت الكتابة من الحافظة. انسخ يدويًا!",
|
||||||
"download-successful": "تم تحميل {{descriptor}}",
|
"download-successful": "تم تحميل {{descriptor}}",
|
||||||
"click-to-show": "اضغط للعرض"
|
"click-to-show": "اضغط للعرض"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"cancel-paste-mode": "تمّ",
|
"cancel-share-mode": "تمّ",
|
||||||
"theme-auto_title": "تكيٌف المظهر مع النظام",
|
"theme-auto_title": "تغيير المظهر تلقائيا من النظام",
|
||||||
"install_title": "تثبيت PairDrop",
|
"install_title": "تثبيت PairDrop",
|
||||||
"theme-dark_title": "إستخدام دائما المظهر المظلم",
|
"theme-dark_title": "إستخدم دائما المظهر المظلم",
|
||||||
"pair-device_title": "قم بإقران أجهزتك بشكل دائم",
|
"pair-device_title": "قم بإقران أجهزتك بشكل دائم",
|
||||||
"join-public-room_title": "انضم إلى الغرفة العامة مؤقتًا",
|
"join-public-room_title": "انضم إلى الغرفة العامة مؤقتًا",
|
||||||
"notification_title": "تشغيل الإشعارات",
|
"notification_title": "تفعيل الإشعارات",
|
||||||
"edit-paired-devices_title": "تعديل الأجهزة المقترنة",
|
"edit-paired-devices_title": "تعديل الأجهزة المقترنة",
|
||||||
"language-selector_title": "إختر اللغةعربي",
|
"language-selector_title": "إختر اللغة",
|
||||||
"about_title": "حول PairDrop",
|
"about_title": "حول PairDrop",
|
||||||
"about_aria-label": "افتح حول PairDrop",
|
"about_aria-label": "افتح حول PairDrop",
|
||||||
"theme-light_title": "إستخدم دائماً المظهر الفاتح"
|
"theme-light_title": "إستخدم دائماً المظهر الفاتح"
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_mobile": "انقر لإرسال الملفات أو انقر لفترة طويلة لإرسال رسالة",
|
"x-instructions_mobile": "انقر لإرسال الملفات أو انقر لفترة طويلة لإرسال رسالة",
|
||||||
"click-to-send": "انقر للإرسال",
|
"x-instructions-share-mode_desktop": "انقر للإرسال",
|
||||||
"activate-paste-mode-and-other-files": "و{{count}} ملفات أخرى",
|
"activate-share-mode-and-other-files-plural": "و{{count}} ملفات أخرى",
|
||||||
"tap-to-send": "انقر للإرسال",
|
"x-instructions-share-mode_mobile": "انقر للإرسال",
|
||||||
"activate-paste-mode-base": "افتح PairDrop على الأجهزة الأخرى للإرسال",
|
"activate-share-mode-base": "افتح PairDrop على الأجهزة الأخرى للإرسال",
|
||||||
"no-peers-subtitle": "قم بإقران الأجهزة أو ادخل إلى غرفة عامة لتتمكن من إكتشافها على الشبكات الأخرى",
|
"no-peers-subtitle": "قم بإقران الأجهزة أو ادخل إلى غرفة عامة لتتمكن من أن تكتشف على الشبكات الأخرى",
|
||||||
"activate-paste-mode-shared-text": "النص المشترك",
|
"activate-share-mode-shared-text": "النص المشترك",
|
||||||
"x-instructions_desktop": "انقر لإرسال الملفات أو انقر بزر الماوس الأيمن لإرسال رسالة",
|
"x-instructions_desktop": "انقر لإرسال الملفات أو انقر بزر الفأرة الأيمن لإرسال رسالة",
|
||||||
"no-peers-title": "افتح PairDrop على الأجهزة الأخرى لإرسال الملفات",
|
"no-peers-title": "افتح PairDrop على الأجهزة الأخرى لإرسال الملفات",
|
||||||
"x-instructions_data-drop-bg": "حرر لتحديد المستلم",
|
"x-instructions_data-drop-bg": "حرر لتحديد المستلم",
|
||||||
"no-peers_data-drop-bg": "حرر لتحديد المستلم",
|
"no-peers_data-drop-bg": "حرر لتحديد المستلم",
|
||||||
"x-instructions_data-drop-peer": "قم بالتحرير لإرسالها إلى النظير"
|
"x-instructions_data-drop-peer": "قم بالتحرير لإرسالها إلى القرين"
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"processing": "مُعالجة …",
|
"processing": "مُعالجة …",
|
||||||
"click-to-send-paste-mode": "انقر للإرسال {{descriptor}}",
|
"click-to-send-share-mode": "انقر للإرسال {{descriptor}}",
|
||||||
"click-to-send": "انقر لإرسال الملفات أو انقر بزر الماوس الأيمن لإرسال رسالة",
|
"click-to-send": "انقر لإرسال الملفات أو انقر بزر الماوس الأيمن لإرسال رسالة",
|
||||||
"waiting": "يُرجى الإنتظار…",
|
"waiting": "يُرجى الإنتظار…",
|
||||||
"connection-hash": "للتحقق من أمان التشفير الشامل، قم بمقارنة رقم الأمان هذا على كلا الجهازين",
|
"connection-hash": "للتحقق من أمان التشفير الشامل، قم بمقارنة رقم الأمان هذا على كلا الجهازين",
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
"auto-accept": "قبول تلقائي",
|
"auto-accept": "قبول تلقائي",
|
||||||
"title-file-plural": "ملفات",
|
"title-file-plural": "ملفات",
|
||||||
"send-message-title": "إرسال رسالة",
|
"send-message-title": "إرسال رسالة",
|
||||||
"input-room-id-on-another-device": "أدخل معرف الغرفة هذا على جهاز آخر ما ",
|
"input-room-id-on-another-device": "أدخل معرف الغرفة هذا على جهاز آخر",
|
||||||
"file-other-description-image-plural": "و{{count}} صور أخرى",
|
"file-other-description-image-plural": "و{{count}} صور أخرى",
|
||||||
"enter-room-id-from-another-device": "أدخل معرف الغرفة من جهاز آخر للانضمام إلى الغرفة."
|
"enter-room-id-from-another-device": "أدخل معرف الغرفة من جهاز آخر للانضمام إلى الغرفة."
|
||||||
},
|
},
|
||||||
|
|
26
public/lang/ca.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"instructions": {
|
||||||
|
"x-instructions_mobile": "Toca per enviar fitxers o mantén premut per enviar un missatge",
|
||||||
|
"no-peers-subtitle": "Vincula dispositius o entra a una sala pública per ser detectable en altres xarxes",
|
||||||
|
"x-instructions_desktop": "Fes click per enviar fitxers o click dret per enviar un missatge de text",
|
||||||
|
"no-peers-title": "Obre PairDrop a altres dispositius per enviar arxius",
|
||||||
|
"x-instructions_data-drop-peer": "Allibera per enviar a un company",
|
||||||
|
"x-instructions_data-drop-bg": "Allibera per seleccionar recipient",
|
||||||
|
"no-peers_data-drop-bg": "Allibera per seleccionar destinatari"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"theme-auto_title": "Adapta el tema al del sistema automàticament",
|
||||||
|
"install_title": "Instal·la PairDrop",
|
||||||
|
"theme-dark_title": "Utilitza sempre el mode fosc",
|
||||||
|
"pair-device_title": "Vincula els teus dispositius permanentment",
|
||||||
|
"join-public-room_title": "Uneix-te temporalment a una sala pública",
|
||||||
|
"notification_title": "Permet les notificacions",
|
||||||
|
"edit-paired-devices_title": "Edita els dispositius vinculats",
|
||||||
|
"edit-share-mode": "Editar",
|
||||||
|
"language-selector_title": "Configurar idioma",
|
||||||
|
"cancel-share-mode": "Cancel·lar",
|
||||||
|
"about_title": "Sobre PairDrop",
|
||||||
|
"about_aria-label": "Obre Sobre PairDrop",
|
||||||
|
"theme-light_title": "Utilitza sempre el mode clar"
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,18 +4,19 @@
|
||||||
"notification_title": "Benachrichtigungen aktivieren",
|
"notification_title": "Benachrichtigungen aktivieren",
|
||||||
"about_aria-label": "Über PairDrop öffnen",
|
"about_aria-label": "Über PairDrop öffnen",
|
||||||
"install_title": "PairDrop installieren",
|
"install_title": "PairDrop installieren",
|
||||||
"pair-device_title": "Deine Geräte dauerhaft koppeln",
|
"pair-device_title": "Kopple deine Geräte dauerhaft",
|
||||||
"edit-paired-devices_title": "Gekoppelte Geräte bearbeiten",
|
"edit-paired-devices_title": "Gekoppelte Geräte bearbeiten",
|
||||||
"theme-auto_title": "Systemstil verwenden",
|
"theme-auto_title": "Systemstil verwenden",
|
||||||
"theme-dark_title": "Immer dunklen Stil verwenden",
|
"theme-dark_title": "Immer dunklen Stil verwenden",
|
||||||
"theme-light_title": "Immer hellen Stil verwenden",
|
"theme-light_title": "Immer hellen Stil verwenden",
|
||||||
"cancel-paste-mode": "Fertig",
|
"cancel-share-mode": "Fertig",
|
||||||
"language-selector_title": "Sprache Wählen",
|
"language-selector_title": "Sprache Wählen",
|
||||||
"join-public-room_title": "Öffentlichen Raum temporär betreten"
|
"join-public-room_title": "Öffentlichen Raum temporär betreten",
|
||||||
|
"edit-share-mode": "Bearbeiten"
|
||||||
},
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
"share": "Teilen",
|
"share": "Teilen",
|
||||||
"download": "Herunterladen",
|
"download": "Download",
|
||||||
"pair-devices-title": "Geräte Dauerhaft Koppeln",
|
"pair-devices-title": "Geräte Dauerhaft Koppeln",
|
||||||
"input-key-on-this-device": "Gib diesen Schlüssel auf einem anderen Gerät ein",
|
"input-key-on-this-device": "Gib diesen Schlüssel auf einem anderen Gerät ein",
|
||||||
"enter-key-from-another-device": "Gib den Schlüssel von einem anderen Gerät hier ein.",
|
"enter-key-from-another-device": "Gib den Schlüssel von einem anderen Gerät hier ein.",
|
||||||
|
@ -34,24 +35,24 @@
|
||||||
"would-like-to-share": "möchte Folgendes teilen",
|
"would-like-to-share": "möchte Folgendes teilen",
|
||||||
"send": "Senden",
|
"send": "Senden",
|
||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
"receive-text-title": "Textnachricht Erhalten",
|
"receive-text-title": "Nachricht Erhalten",
|
||||||
"file-other-description-image-plural": "und {{count}} andere Bilder",
|
"file-other-description-image-plural": "und {{count}} andere Bilder",
|
||||||
"file-other-description-file-plural": "und {{count}} andere Dateien",
|
"file-other-description-file-plural": "und {{count}} andere Dateien",
|
||||||
"auto-accept-instructions-1": "Aktiviere",
|
"auto-accept-instructions-1": "Aktiviere",
|
||||||
"auto-accept": "automatisch-akzeptieren",
|
"auto-accept": "automatisch-akzeptieren",
|
||||||
"auto-accept-instructions-2": "um automatisch alle Dateien von diesem Gerät zu akzeptieren.",
|
"auto-accept-instructions-2": "um automatisch alle Dateien von diesem Gerät zu akzeptieren.",
|
||||||
"has-sent": "hat Folgendes gesendet:",
|
"has-sent": "hat Folgendes gesendet:",
|
||||||
"send-message-title": "Textnachricht Senden",
|
"send-message-title": "Nachricht Senden",
|
||||||
"send-message-to": "Sende eine Textnachricht an",
|
"send-message-to": "An:",
|
||||||
"base64-tap-to-paste": "Hier tippen, um {{type}} einzufügen",
|
"base64-tap-to-paste": "Hier tippen, um {{type}} zu teilen",
|
||||||
"base64-paste-to-send": "Hier einfügen, um {{type}} zu versenden",
|
"base64-paste-to-send": "Hier einfügen, um {{type}} zu teilen",
|
||||||
"base64-text": "Text",
|
"base64-text": "Text",
|
||||||
"base64-files": "Dateien",
|
"base64-files": "Dateien",
|
||||||
"base64-processing": "Bearbeitung läuft…",
|
"base64-processing": "Bearbeitung läuft…",
|
||||||
"file-other-description-image": "und ein anderes Bild",
|
"file-other-description-image": "und ein anderes Bild",
|
||||||
"file-other-description-file": "und eine andere Datei",
|
"file-other-description-file": "und eine andere Datei",
|
||||||
"receive-title": "{{descriptor}} Erhalten",
|
"receive-title": "{{descriptor}} Erhalten",
|
||||||
"download-again": "Erneut herunterladen",
|
"download-again": "Erneuter Download",
|
||||||
"system-language": "Systemsprache",
|
"system-language": "Systemsprache",
|
||||||
"language-selector-title": "Sprache Einstellen",
|
"language-selector-title": "Sprache Einstellen",
|
||||||
"hr-or": "ODER",
|
"hr-or": "ODER",
|
||||||
|
@ -63,7 +64,16 @@
|
||||||
"temporary-public-room-title": "Temporärer Öffentlicher Raum",
|
"temporary-public-room-title": "Temporärer Öffentlicher Raum",
|
||||||
"message_title": "Nachricht zum Senden hier einfügen",
|
"message_title": "Nachricht zum Senden hier einfügen",
|
||||||
"pair-devices-qr-code_title": "Klicke, um Link zum Koppeln mit diesem Gerät zu kopieren",
|
"pair-devices-qr-code_title": "Klicke, um Link zum Koppeln mit diesem Gerät zu kopieren",
|
||||||
"public-room-qr-code_title": "Klicke, um Link zu diesem öffentlichen Raum zu kopieren"
|
"public-room-qr-code_title": "Klicke, um Link zu diesem öffentlichen Raum zu kopieren",
|
||||||
|
"message_placeholder": "Text",
|
||||||
|
"close-toast_title": "Benachrichtigung schließen",
|
||||||
|
"share-text-checkbox": "Diesen Dialog immer anzeigen, wenn Text geteilt wird",
|
||||||
|
"base64-title-files": "Teile Dateien",
|
||||||
|
"approve": "bestätigen",
|
||||||
|
"paired-device-removed": "Gekoppeltes Gerät wurde entfernt.",
|
||||||
|
"share-text-title": "Teile Nachricht",
|
||||||
|
"share-text-subtitle": "Bearbeite Nachricht vor dem Senden:",
|
||||||
|
"base64-title-text": "Teile Text"
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"tweet_title": "Über PairDrop twittern",
|
"tweet_title": "Über PairDrop twittern",
|
||||||
|
@ -75,7 +85,7 @@
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"known-as": "Du wirst angezeigt als:",
|
"known-as": "Du wirst angezeigt als:",
|
||||||
"display-name_title": "Setze einen permanenten Gerätenamen",
|
"display-name_title": "Ändere deinen Gerätenamen dauerhaft",
|
||||||
"on-this-network": "in diesem Netzwerk",
|
"on-this-network": "in diesem Netzwerk",
|
||||||
"paired-devices": "für gekoppelte Geräte",
|
"paired-devices": "für gekoppelte Geräte",
|
||||||
"traffic": "Datenverkehr wird",
|
"traffic": "Datenverkehr wird",
|
||||||
|
@ -83,7 +93,7 @@
|
||||||
"routed": "durch den Server geleitet",
|
"routed": "durch den Server geleitet",
|
||||||
"webrtc": "wenn WebRTC nicht verfügbar ist.",
|
"webrtc": "wenn WebRTC nicht verfügbar ist.",
|
||||||
"display-name_data-placeholder": "Lade…",
|
"display-name_data-placeholder": "Lade…",
|
||||||
"public-room-devices_title": "Du kannst von Geräten in diesem öffentlichen Raum gefunden werden, unabhängig von deinem Netzwerk.",
|
"public-room-devices_title": "Du kannst von Geräten in diesem öffentlichen Raum gefunden werden, egal in welchem Netzwerk.",
|
||||||
"paired-devices_title": "Du kannst immer von gekoppelten Geräten gefunden werden, egal in welchem Netzwerk.",
|
"paired-devices_title": "Du kannst immer von gekoppelten Geräten gefunden werden, egal in welchem Netzwerk.",
|
||||||
"public-room-devices": "in Raum {{roomId}}",
|
"public-room-devices": "in Raum {{roomId}}",
|
||||||
"discovery": "Du bist sichtbar:",
|
"discovery": "Du bist sichtbar:",
|
||||||
|
@ -94,72 +104,76 @@
|
||||||
"message-received": "Nachricht von {{name}} empfangen - Klicke um sie zu kopieren",
|
"message-received": "Nachricht von {{name}} empfangen - Klicke um sie zu kopieren",
|
||||||
"click-to-download": "Klicken zum Download",
|
"click-to-download": "Klicken zum Download",
|
||||||
"copied-text": "Text in die Zwischenablage kopiert",
|
"copied-text": "Text in die Zwischenablage kopiert",
|
||||||
"connected": "Verbunden.",
|
"connected": "Verbunden",
|
||||||
"pairing-success": "Geräte gekoppelt.",
|
"pairing-success": "Geräte gekoppelt",
|
||||||
"display-name-random-again": "Anzeigename wird ab jetzt wieder zufällig generiert.",
|
"display-name-random-again": "Anzeigename wird ab jetzt wieder zufällig generiert",
|
||||||
"pairing-tabs-error": "Es können keine zwei Webbrowser Tabs gekoppelt werden.",
|
"pairing-tabs-error": "Es können keine zwei Webbrowser Tabs gekoppelt werden",
|
||||||
"pairing-not-persistent": "Gekoppelte Geräte sind nicht persistent.",
|
"pairing-not-persistent": "Gekoppelte Geräte sind nicht persistent",
|
||||||
"pairing-key-invalid": "Ungültiger Schlüssel",
|
"pairing-key-invalid": "Ungültiger Schlüssel",
|
||||||
"pairing-key-invalidated": "Schlüssel {{key}} wurde ungültig gemacht.",
|
"pairing-key-invalidated": "Schlüssel {{key}} wurde ungültig gemacht",
|
||||||
"copied-to-clipboard": "In die Zwischenablage kopiert",
|
"copied-to-clipboard": "In die Zwischenablage kopiert",
|
||||||
"text-content-incorrect": "Textinhalt ist fehlerhaft.",
|
"text-content-incorrect": "Textinhalt ist fehlerhaft",
|
||||||
"clipboard-content-incorrect": "Inhalt der Zwischenablage ist fehlerhaft.",
|
"clipboard-content-incorrect": "Inhalt der Zwischenablage ist fehlerhaft",
|
||||||
"copied-text-error": "Konnte nicht in die Zwischenablage schreiben. Kopiere manuell!",
|
"copied-text-error": "Konnte nicht in die Zwischenablage schreiben. Kopiere manuell!",
|
||||||
"file-content-incorrect": "Dateiinhalt ist fehlerhaft.",
|
"file-content-incorrect": "Dateiinhalt ist fehlerhaft",
|
||||||
"notifications-enabled": "Benachrichtigungen aktiviert.",
|
"notifications-enabled": "Benachrichtigungen aktiviert",
|
||||||
"offline": "Du bist offline",
|
"offline": "Du bist offline",
|
||||||
"online": "Du bist wieder Online",
|
"online": "Du bist wieder Online",
|
||||||
"unfinished-transfers-warning": "Es wurden noch nicht alle Übertragungen fertiggestellt. Möchtest du PairDrop wirklich schließen?",
|
"unfinished-transfers-warning": "Es wurden noch nicht alle Übertragungen fertiggestellt. Möchtest du PairDrop wirklich schließen?",
|
||||||
"display-name-changed-permanently": "Anzeigename wurde dauerhaft geändert.",
|
"display-name-changed-permanently": "Anzeigename wurde dauerhaft geändert",
|
||||||
"download-successful": "{{descriptor}} heruntergeladen",
|
"download-successful": "{{descriptor}} heruntergeladen",
|
||||||
"pairing-cleared": "Alle Geräte entkoppelt.",
|
"pairing-cleared": "Alle Geräte entkoppelt",
|
||||||
"click-to-show": "Klicken zum Anzeigen",
|
"click-to-show": "Klicken zum Anzeigen",
|
||||||
"online-requirement": "Du musst online sein um Geräte zu koppeln.",
|
"online-requirement": "Du musst online sein um Geräte zu koppeln.",
|
||||||
"display-name-changed-temporarily": "Anzeigename wurde nur für diese Session geändert.",
|
"display-name-changed-temporarily": "Anzeigename wurde nur für diese Session geändert",
|
||||||
"request-title": "{{name}} möchte {{count}}{{descriptor}} übertragen",
|
"request-title": "{{name}} möchte {{count}}{{descriptor}} übertragen",
|
||||||
"connecting": "Verbindung wird hergestellt…",
|
"connecting": "Verbindung wird hergestellt…",
|
||||||
"files-incorrect": "Dateien sind fehlerhaft.",
|
"files-incorrect": "Dateien sind fehlerhaft",
|
||||||
"file-transfer-completed": "Dateiübertragung abgeschlossen.",
|
"file-transfer-completed": "Dateitransfer abgeschlossen",
|
||||||
"message-transfer-completed": "Nachrichtenübertragung fertiggestellt.",
|
"message-transfer-completed": "Nachricht übertragen",
|
||||||
"rate-limit-join-key": "Rate Limit erreicht. Warte 10 Sekunden und versuche es erneut.",
|
"rate-limit-join-key": "Rate Limit erreicht. Warte 10 Sekunden und versuche es erneut.",
|
||||||
"selected-peer-left": "Ausgewählter Peer ist gegangen.",
|
"selected-peer-left": "Ausgewählter Peer ist gegangen",
|
||||||
"ios-memory-limit": "Für Übertragungen an iOS Geräte beträgt die maximale Dateigröße 200 MB",
|
"ios-memory-limit": "Für Übertragungen an iOS Geräte beträgt die maximale Dateigröße 200 MB",
|
||||||
"public-room-left": "Öffentlichen Raum {{publicRoomId}} verlassen",
|
"public-room-left": "Öffentlichen Raum {{publicRoomId}} verlassen",
|
||||||
"copied-to-clipboard-error": "Konnte nicht kopieren. Kopiere manuell.",
|
"copied-to-clipboard-error": "Konnte nicht kopieren. Kopiere manuell.",
|
||||||
"public-room-id-invalid": "Ungültige Raum-ID",
|
"public-room-id-invalid": "Ungültige Raum-ID",
|
||||||
"online-requirement-pairing": "Du musst online sein, um Geräte zu koppeln.",
|
"online-requirement-pairing": "Du musst online sein, um Geräte zu koppeln",
|
||||||
"online-requirement-public-room": "Du musst online sein, um öffentliche Räume erstellen zu können.",
|
"online-requirement-public-room": "Du musst online sein, um öffentliche Räume erstellen zu können",
|
||||||
"notifications-permissions-error": "Benachrichtigungen wurden blockiert, weil der Nutzer die Berechtigungsanfrage mehrfach abgelehnt hat. Dies kann in den Einstellungen der Website zurückgesetzt werden, welche durch Klick auf das Schloss Symbol neben der URL Leiste erreicht werden können.",
|
"notifications-permissions-error": "Benachrichtigungen wurden blockiert, weil der Nutzer die Berechtigungsanfrage mehrfach abgelehnt hat. Dies kann in den Einstellungen der Website zurückgesetzt werden, welche durch Klick auf das Schloss Symbol neben der URL Leiste erreicht werden können.",
|
||||||
"pair-url-copied-to-clipboard": "Link zum Koppeln mit diesem Gerät in Zwischenablage kopiert",
|
"pair-url-copied-to-clipboard": "Link zum Koppeln mit diesem Gerät in Zwischenablage kopiert",
|
||||||
"room-url-copied-to-clipboard": "Link zu diesem öffentlichen Raum in Zwischenablage kopiert"
|
"room-url-copied-to-clipboard": "Link zu diesem öffentlichen Raum in Zwischenablage kopiert"
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_desktop": "Klicke, um Dateien zu senden oder benutze einen Rechtsklick, um eine Textnachricht zu senden",
|
"x-instructions_desktop": "Klicke, um Dateien zu senden oder benutze einen Rechtsklick, um eine Nachricht zu senden",
|
||||||
"no-peers-title": "Öffne PairDrop auf anderen Geräten, um Dateien zu senden",
|
"no-peers-title": "Öffne PairDrop auf anderen Geräten, um Dateien zu senden",
|
||||||
"no-peers_data-drop-bg": "Hier ablegen, um Empfänger auszuwählen",
|
"no-peers_data-drop-bg": "Hier ablegen, um Empfänger auszuwählen",
|
||||||
"no-peers-subtitle": "Kopple Geräte oder besuche einen öffentlichen Raum, damit du in anderen Netzwerken sichtbar bist",
|
"no-peers-subtitle": "Kopple Geräte oder betrete einen öffentlichen Raum, um in anderen Netzwerken sichtbar zu sein",
|
||||||
"click-to-send": "Klicke zum Senden von",
|
"x-instructions-share-mode_desktop": "Klicke zum Senden von {{descriptor}}",
|
||||||
"tap-to-send": "Tippe zum Senden von",
|
"x-instructions-share-mode_mobile": "Tippe zum Senden von {{descriptor}}",
|
||||||
"x-instructions_data-drop-peer": "Hier ablegen, um an Peer zu senden",
|
"x-instructions_data-drop-peer": "Hier ablegen, um an Peer zu senden",
|
||||||
"x-instructions_data-drop-bg": "Loslassen um Empfänger auszuwählen",
|
"x-instructions_data-drop-bg": "Loslassen um Empfänger auszuwählen",
|
||||||
"x-instructions_mobile": "Tippe, um Dateien zu senden oder tippe lange, um Nachrichten zu senden",
|
"x-instructions_mobile": "Tippe, um Dateien zu senden oder tippe lange, um Nachrichten zu senden",
|
||||||
"activate-paste-mode-base": "Öffne PairDrop auf anderen Geräten zum Senden von",
|
"activate-share-mode-base": "Öffne PairDrop auf anderen Geräten zum Senden von",
|
||||||
"activate-paste-mode-and-other-files": "und {{count}} anderen Dateien",
|
"activate-share-mode-and-other-files-plural": "und {{count}} anderen Dateien",
|
||||||
"activate-paste-mode-shared-text": "freigegebenem Text"
|
"activate-share-mode-shared-text": "freigegebenem Text",
|
||||||
|
"webrtc-requirement": "Um diese PairDrop Instanz zu verwenden muss WebRTC aktiviert sein!",
|
||||||
|
"activate-share-mode-shared-files-plural": "{{count}} geteilte Dateien",
|
||||||
|
"activate-share-mode-shared-file": "geteilte Datei",
|
||||||
|
"activate-share-mode-and-other-file": "und 1 andere Datei"
|
||||||
},
|
},
|
||||||
"document-titles": {
|
"document-titles": {
|
||||||
"file-transfer-requested": "Dateiübertragung angefordert",
|
"file-transfer-requested": "Dateitransfer beantragt",
|
||||||
"file-received": "Datei erhalten",
|
"file-received": "Datei erhalten",
|
||||||
"file-received-plural": "{{count}} Dateien erhalten",
|
"file-received-plural": "{{count}} Dateien erhalten",
|
||||||
"message-received": "Nachricht erhalten",
|
"message-received": "Nachricht erhalten",
|
||||||
"message-received-plural": "{{count}} Nachrichten erhalten",
|
"message-received-plural": "{{count}} Nachrichten erhalten",
|
||||||
"image-transfer-requested": "Bilder Transfer beantragt"
|
"image-transfer-requested": "Transfer von Bildern beantragt"
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"click-to-send": "Klicke, um Dateien zu senden oder benutze einen Rechtsklick, um eine Textnachricht zu senden",
|
"click-to-send": "Klicke, um Dateien zu senden oder benutze einen Rechtsklick, um eine Textnachricht zu senden",
|
||||||
"connection-hash": "Um die Ende-zu-Ende Verschlüsselung zu verifizieren, vergleiche die Sicherheitsnummer auf beiden Geräten",
|
"connection-hash": "Um die Ende-zu-Ende Verschlüsselung zu verifizieren, vergleiche die Sicherheitsnummer auf beiden Geräten",
|
||||||
"waiting": "Warte…",
|
"waiting": "Warte…",
|
||||||
"click-to-send-paste-mode": "Klicken um {{descriptor}} zu senden",
|
"click-to-send-share-mode": "Klicken um {{descriptor}} zu senden",
|
||||||
"transferring": "Übertragung läuft…",
|
"transferring": "Übertragung läuft…",
|
||||||
"processing": "Bearbeitung läuft…",
|
"processing": "Bearbeitung läuft…",
|
||||||
"preparing": "Vorbereitung läuft…"
|
"preparing": "Vorbereitung läuft…"
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
"pair-device_title": "Pair your devices permanently",
|
"pair-device_title": "Pair your devices permanently",
|
||||||
"edit-paired-devices_title": "Edit paired devices",
|
"edit-paired-devices_title": "Edit paired devices",
|
||||||
"join-public-room_title": "Join public room temporarily",
|
"join-public-room_title": "Join public room temporarily",
|
||||||
"cancel-paste-mode": "Done"
|
"cancel-share-mode": "Cancel",
|
||||||
|
"edit-share-mode": "Edit"
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"no-peers_data-drop-bg": "Release to select recipient",
|
"no-peers_data-drop-bg": "Release to select recipient",
|
||||||
|
@ -21,11 +22,15 @@
|
||||||
"x-instructions_mobile": "Tap to send files or long tap to send a message",
|
"x-instructions_mobile": "Tap to send files or long tap to send a message",
|
||||||
"x-instructions_data-drop-peer": "Release to send to peer",
|
"x-instructions_data-drop-peer": "Release to send to peer",
|
||||||
"x-instructions_data-drop-bg": "Release to select recipient",
|
"x-instructions_data-drop-bg": "Release to select recipient",
|
||||||
"click-to-send": "Click to send",
|
"x-instructions-share-mode_desktop": "Click to send {{descriptor}}",
|
||||||
"tap-to-send": "Tap to send",
|
"x-instructions-share-mode_mobile": "Tap to send {{descriptor}}",
|
||||||
"activate-paste-mode-base": "Open PairDrop on other devices to send",
|
"activate-share-mode-base": "Open PairDrop on other devices to send",
|
||||||
"activate-paste-mode-and-other-files": "and {{count}} other files",
|
"activate-share-mode-and-other-file": "and 1 other file",
|
||||||
"activate-paste-mode-shared-text": "shared text"
|
"activate-share-mode-and-other-files-plural": "and {{count}} other files",
|
||||||
|
"activate-share-mode-shared-text": "shared text",
|
||||||
|
"activate-share-mode-shared-file": "shared file",
|
||||||
|
"activate-share-mode-shared-files-plural": "{{count}} shared files",
|
||||||
|
"webrtc-requirement": "To use this PairDrop instance, WebRTC must be enabled!"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"known-as": "You are known as:",
|
"known-as": "You are known as:",
|
||||||
|
@ -55,6 +60,7 @@
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"edit-paired-devices-title": "Edit Paired Devices",
|
"edit-paired-devices-title": "Edit Paired Devices",
|
||||||
"unpair": "Unpair",
|
"unpair": "Unpair",
|
||||||
|
"paired-device-removed": "Paired device has been removed.",
|
||||||
"paired-devices-wrapper_data-empty": "No paired devices.",
|
"paired-devices-wrapper_data-empty": "No paired devices.",
|
||||||
"auto-accept-instructions-1": "Activate",
|
"auto-accept-instructions-1": "Activate",
|
||||||
"auto-accept": "auto-accept",
|
"auto-accept": "auto-accept",
|
||||||
|
@ -69,14 +75,17 @@
|
||||||
"share": "Share",
|
"share": "Share",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"send-message-title": "Send Message",
|
"send-message-title": "Send Message",
|
||||||
"send-message-to": "Send a Message to",
|
"send-message-to": "To:",
|
||||||
"message_title": "Insert message to send",
|
"message_title": "Insert message to send",
|
||||||
|
"message_placeholder": "Text",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
"receive-text-title": "Message Received",
|
"receive-text-title": "Message Received",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
|
"base64-title-files": "Share Files",
|
||||||
|
"base64-title-text": "Share Text",
|
||||||
"base64-processing": "Processing…",
|
"base64-processing": "Processing…",
|
||||||
"base64-tap-to-paste": "Tap here to paste {{type}}",
|
"base64-tap-to-paste": "Tap here to share {{type}}",
|
||||||
"base64-paste-to-send": "Paste here to send {{type}}",
|
"base64-paste-to-send": "Paste clipboard here to share {{type}}",
|
||||||
"base64-text": "text",
|
"base64-text": "text",
|
||||||
"base64-files": "files",
|
"base64-files": "files",
|
||||||
"file-other-description-image": "and 1 other image",
|
"file-other-description-image": "and 1 other image",
|
||||||
|
@ -92,7 +101,12 @@
|
||||||
"language-selector-title": "Set Language",
|
"language-selector-title": "Set Language",
|
||||||
"system-language": "System Language",
|
"system-language": "System Language",
|
||||||
"public-room-qr-code_title": "Click to copy link to public room",
|
"public-room-qr-code_title": "Click to copy link to public room",
|
||||||
"pair-devices-qr-code_title": "Click to copy link to pair this device"
|
"pair-devices-qr-code_title": "Click to copy link to pair this device",
|
||||||
|
"approve": "approve",
|
||||||
|
"share-text-title": "Share Text Message",
|
||||||
|
"share-text-subtitle": "Edit message before sending:",
|
||||||
|
"share-text-checkbox": "Always show this dialog when sharing text",
|
||||||
|
"close-toast_title": "Close notification"
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"close-about_aria-label": "Close About PairDrop",
|
"close-about_aria-label": "Close About PairDrop",
|
||||||
|
@ -103,26 +117,26 @@
|
||||||
"faq_title": "Frequently asked questions"
|
"faq_title": "Frequently asked questions"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"display-name-changed-permanently": "Display name is changed permanently.",
|
"display-name-changed-permanently": "Display name is changed permanently",
|
||||||
"display-name-changed-temporarily": "Display name is changed only for this session.",
|
"display-name-changed-temporarily": "Display name is changed for this session only",
|
||||||
"display-name-random-again": "Display name is randomly generated again.",
|
"display-name-random-again": "Display name is randomly generated again",
|
||||||
"download-successful": "{{descriptor}} downloaded",
|
"download-successful": "{{descriptor}} downloaded",
|
||||||
"pairing-tabs-error": "Pairing two web browser tabs is impossible.",
|
"pairing-tabs-error": "Pairing two web browser tabs is impossible",
|
||||||
"pairing-success": "Devices paired.",
|
"pairing-success": "Devices paired",
|
||||||
"pairing-not-persistent": "Paired devices are not persistent.",
|
"pairing-not-persistent": "Paired devices are not persistent",
|
||||||
"pairing-key-invalid": "Invalid key",
|
"pairing-key-invalid": "Invalid key",
|
||||||
"pairing-key-invalidated": "Key {{key}} invalidated.",
|
"pairing-key-invalidated": "Key {{key}} invalidated",
|
||||||
"pairing-cleared": "All Devices unpaired.",
|
"pairing-cleared": "All devices unpaired",
|
||||||
"public-room-id-invalid": "Invalid room ID",
|
"public-room-id-invalid": "Invalid room ID",
|
||||||
"public-room-left": "Left public room {{publicRoomId}}",
|
"public-room-left": "Left public room {{publicRoomId}}",
|
||||||
"copied-to-clipboard": "Copied to clipboard",
|
"copied-to-clipboard": "Copied to clipboard",
|
||||||
"pair-url-copied-to-clipboard": "Link to pair this device copied to clipboard",
|
"pair-url-copied-to-clipboard": "Link to pair this device copied to clipboard",
|
||||||
"room-url-copied-to-clipboard": "Link to public room copied to clipboard",
|
"room-url-copied-to-clipboard": "Link to public room copied to clipboard",
|
||||||
"copied-to-clipboard-error": "Copying not possible. Copy manually.",
|
"copied-to-clipboard-error": "Copying not possible. Copy manually.",
|
||||||
"text-content-incorrect": "Text content is incorrect.",
|
"text-content-incorrect": "Text content is incorrect",
|
||||||
"file-content-incorrect": "File content is incorrect.",
|
"file-content-incorrect": "File content is incorrect",
|
||||||
"clipboard-content-incorrect": "Clipboard content is incorrect.",
|
"clipboard-content-incorrect": "Clipboard content is incorrect",
|
||||||
"notifications-enabled": "Notifications enabled.",
|
"notifications-enabled": "Notifications enabled",
|
||||||
"notifications-permissions-error": "Notifications permission has been blocked as the user has dismissed the permission prompt several times. This can be reset in Page Info which can be accessed by clicking the lock icon next to the URL bar.",
|
"notifications-permissions-error": "Notifications permission has been blocked as the user has dismissed the permission prompt several times. This can be reset in Page Info which can be accessed by clicking the lock icon next to the URL bar.",
|
||||||
"link-received": "Link received by {{name}} - Click to open",
|
"link-received": "Link received by {{name}} - Click to open",
|
||||||
"message-received": "Message received by {{name}} - Click to copy",
|
"message-received": "Message received by {{name}} - Click to copy",
|
||||||
|
@ -133,17 +147,17 @@
|
||||||
"copied-text-error": "Writing to clipboard failed. Copy manually!",
|
"copied-text-error": "Writing to clipboard failed. Copy manually!",
|
||||||
"offline": "You are offline",
|
"offline": "You are offline",
|
||||||
"online": "You are back online",
|
"online": "You are back online",
|
||||||
"connected": "Connected.",
|
"connected": "Connected",
|
||||||
"online-requirement-pairing": "You need to be online to pair devices.",
|
"online-requirement-pairing": "You need to be online to pair devices",
|
||||||
"online-requirement-public-room": "You need to be online to create a public room.",
|
"online-requirement-public-room": "You need to be online to create a public room",
|
||||||
"connecting": "Connecting…",
|
"connecting": "Connecting…",
|
||||||
"files-incorrect": "Files are incorrect.",
|
"files-incorrect": "Files are incorrect",
|
||||||
"file-transfer-completed": "File transfer completed.",
|
"file-transfer-completed": "File transfer completed",
|
||||||
"ios-memory-limit": "Sending files to iOS is only possible up to 200 MB at once",
|
"ios-memory-limit": "Sending files to iOS is only possible up to 200 MB at once",
|
||||||
"message-transfer-completed": "Message transfer completed.",
|
"message-transfer-completed": "Message transfer completed",
|
||||||
"unfinished-transfers-warning": "There are unfinished transfers. Are you sure you want to close PairDrop?",
|
"unfinished-transfers-warning": "There are unfinished transfers. Are you sure you want to close PairDrop?",
|
||||||
"rate-limit-join-key": "Rate limit reached. Wait 10 seconds and try again.",
|
"rate-limit-join-key": "Rate limit reached. Wait 10 seconds and try again.",
|
||||||
"selected-peer-left": "Selected peer left."
|
"selected-peer-left": "Selected peer left"
|
||||||
},
|
},
|
||||||
"document-titles": {
|
"document-titles": {
|
||||||
"file-received": "File Received",
|
"file-received": "File Received",
|
||||||
|
@ -154,7 +168,7 @@
|
||||||
"message-received-plural": "{{count}} Messages Received"
|
"message-received-plural": "{{count}} Messages Received"
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"click-to-send-paste-mode": "Click to send {{descriptor}}",
|
"click-to-send-share-mode": "Click to send {{descriptor}}",
|
||||||
"click-to-send": "Click to send files or right click to send a message",
|
"click-to-send": "Click to send files or right click to send a message",
|
||||||
"connection-hash": "To verify the security of the end-to-end encryption, compare this security number on both devices",
|
"connection-hash": "To verify the security of the end-to-end encryption, compare this security number on both devices",
|
||||||
"preparing": "Preparing…",
|
"preparing": "Preparing…",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"language-selector_title": "Configurar Idioma",
|
"language-selector_title": "Configurar Idioma",
|
||||||
"about_title": "Sobre PairDrop",
|
"about_title": "Sobre PairDrop",
|
||||||
"about_aria-label": "Abrir Sobre PairDrop",
|
"about_aria-label": "Abrir Sobre PairDrop",
|
||||||
"cancel-paste-mode": "Listo",
|
"cancel-share-mode": "Listo",
|
||||||
"install_title": "Instalar PairDrop",
|
"install_title": "Instalar PairDrop",
|
||||||
"theme-dark_title": "Siempre usar tema oscuro",
|
"theme-dark_title": "Siempre usar tema oscuro",
|
||||||
"pair-device_title": "Empareja tus dispositivos permanentemente",
|
"pair-device_title": "Empareja tus dispositivos permanentemente",
|
||||||
|
@ -34,36 +34,36 @@
|
||||||
"message-received": "Mensaje recibido por {{name}} - Haga clic para copiar",
|
"message-received": "Mensaje recibido por {{name}} - Haga clic para copiar",
|
||||||
"rate-limit-join-key": "Límite de intentos alcanzado. Espere 10 segundos y vuelva a intentarlo.",
|
"rate-limit-join-key": "Límite de intentos alcanzado. Espere 10 segundos y vuelva a intentarlo.",
|
||||||
"connecting": "Conectando…",
|
"connecting": "Conectando…",
|
||||||
"pairing-key-invalidated": "Clave {{key}} invalidada.",
|
"pairing-key-invalidated": "Clave {{key}} invalidada",
|
||||||
"pairing-key-invalid": "Clave inválida",
|
"pairing-key-invalid": "Clave inválida",
|
||||||
"connected": "Connectado.",
|
"connected": "Connectado",
|
||||||
"pairing-not-persistent": "Los dispositivos emparejados no son persistentes.",
|
"pairing-not-persistent": "Los dispositivos emparejados no son persistentes",
|
||||||
"text-content-incorrect": "El contenido del texto es incorrecto.",
|
"text-content-incorrect": "El contenido del texto es incorrecto",
|
||||||
"message-transfer-completed": "Transferencia de mensaje completada.",
|
"message-transfer-completed": "Transferencia del mensaje completada",
|
||||||
"file-transfer-completed": "Transferencia de archivos completada.",
|
"file-transfer-completed": "Transferencia de archivos completada",
|
||||||
"file-content-incorrect": "El contenido del archivo es incorrecto.",
|
"file-content-incorrect": "El contenido del archivo es incorrecto",
|
||||||
"files-incorrect": "Los archivos son incorrectos.",
|
"files-incorrect": "Los archivos son incorrectos",
|
||||||
"selected-peer-left": "El dispositivo seleccionado se fue.",
|
"selected-peer-left": "Dispositivos seleccionados restantes",
|
||||||
"link-received": "Link recibido por {{name}} - Haga clic para abrir",
|
"link-received": "Link recibido por {{name}} - Haga clic para abrir",
|
||||||
"online": "Estás de nuevo en línea",
|
"online": "Estás de nuevo en línea",
|
||||||
"public-room-left": "Salió de la sala pública {{publicRoomId}}",
|
"public-room-left": "Salió de la sala pública {{publicRoomId}}",
|
||||||
"copied-text": "Texto copiado al portapapeles",
|
"copied-text": "Texto copiado al portapapeles",
|
||||||
"display-name-random-again": "El nombre mostrado se genera aleatoriamente nuevamente.",
|
"display-name-random-again": "El nombre mostrado se genera aleatoriamente nuevamente",
|
||||||
"display-name-changed-permanently": "El nombre para mostrar se ha cambiado permanentemente.",
|
"display-name-changed-permanently": "El nombre para mostrar se ha cambiado permanentemente",
|
||||||
"copied-to-clipboard-error": "No es posible copiarlo. Cópielo manualmente.",
|
"copied-to-clipboard-error": "No es posible copiarlo. Cópielo manualmente.",
|
||||||
"pairing-success": "Dispositivos emparejados.",
|
"pairing-success": "Dispositivos emparejados",
|
||||||
"clipboard-content-incorrect": "El contenido del portapapeles es incorrecto.",
|
"clipboard-content-incorrect": "El contenido del portapapeles es incorrecto",
|
||||||
"display-name-changed-temporarily": "El nombre mostrado se cambia solo para esta sesión.",
|
"display-name-changed-temporarily": "El nombre para mostrar se cambia sólo para esta sesión",
|
||||||
"copied-to-clipboard": "Copiado al portapapeles",
|
"copied-to-clipboard": "Copiado al portapapeles",
|
||||||
"offline": "Estás desconectado",
|
"offline": "Estás desconectado",
|
||||||
"pairing-tabs-error": "Emparejar dos pestañas del navegador es imposible.",
|
"pairing-tabs-error": "Emparejar dos pestañas del navegador es imposible",
|
||||||
"public-room-id-invalid": "ID de sala no válido",
|
"public-room-id-invalid": "ID de sala no válido",
|
||||||
"click-to-download": "Haga clic para descargar",
|
"click-to-download": "Haga clic para descargar",
|
||||||
"pairing-cleared": "Todos los dispositivos han sido desemparejados.",
|
"pairing-cleared": "Todos los dispositivos han sido desemparejados",
|
||||||
"notifications-enabled": "Notificaciones habilitadas.",
|
"notifications-enabled": "Notificaciones habilitadas",
|
||||||
"online-requirement-pairing": "Debes estar en línea para emparejar dispositivos.",
|
"online-requirement-pairing": "Debes estar en línea para emparejar dispositivos",
|
||||||
"ios-memory-limit": "Enviar archivos a iOS sólo admite hasta 200 MB a la vez",
|
"ios-memory-limit": "Enviar archivos a iOS sólo admite hasta 200 MB a la vez",
|
||||||
"online-requirement-public-room": "Debes estar en línea para crear una sala pública.",
|
"online-requirement-public-room": "Debes estar en línea para crear una sala pública",
|
||||||
"copied-text-error": "Error al escribir en el portapapeles. ¡Cópielo manualmente!",
|
"copied-text-error": "Error al escribir en el portapapeles. ¡Cópielo manualmente!",
|
||||||
"download-successful": "{{descriptor}} descargado",
|
"download-successful": "{{descriptor}} descargado",
|
||||||
"click-to-show": "Click para mostrar",
|
"click-to-show": "Click para mostrar",
|
||||||
|
@ -73,21 +73,22 @@
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_mobile": "Toque para enviar archivos o toque prologádamente para enviar un mensaje",
|
"x-instructions_mobile": "Toque para enviar archivos o toque prologádamente para enviar un mensaje",
|
||||||
"click-to-send": "Haga clic para enviar",
|
"x-instructions-share-mode_desktop": "Haga clic para enviar",
|
||||||
"activate-paste-mode-and-other-files": "y {{count}} archivos diferentes",
|
"activate-share-mode-and-other-files-plural": "y {{count}} archivos diferentes",
|
||||||
"tap-to-send": "Toca para enviar",
|
"x-instructions-share-mode_mobile": "Toca para enviar",
|
||||||
"activate-paste-mode-base": "Abra PairDrop en otros dispositivos para enviar",
|
"activate-share-mode-base": "Abra PairDrop en otros dispositivos para enviar",
|
||||||
"no-peers-subtitle": "Empareje dispositivos o ingrese a una sala pública para que lo puedan encontrar en otras redes",
|
"no-peers-subtitle": "Empareje dispositivos o ingrese a una sala pública para que lo puedan encontrar en otras redes",
|
||||||
"activate-paste-mode-shared-text": "texto compartido",
|
"activate-share-mode-shared-text": "texto compartido",
|
||||||
"x-instructions_desktop": "Haga clic para enviar archivos o haga clic derecho para enviar un mensaje",
|
"x-instructions_desktop": "Haga clic para enviar archivos o haga clic derecho para enviar un mensaje",
|
||||||
"no-peers-title": "Abra PairDrop en otros dispositivos para enviar archivos",
|
"no-peers-title": "Abra PairDrop en otros dispositivos para enviar archivos",
|
||||||
"x-instructions_data-drop-peer": "Liberar para enviar a un par",
|
"x-instructions_data-drop-peer": "Liberar para enviar a un par",
|
||||||
"x-instructions_data-drop-bg": "Liberar para seleccionar destinatario",
|
"x-instructions_data-drop-bg": "Liberar para seleccionar destinatario",
|
||||||
"no-peers_data-drop-bg": "Liberar para seleccionar destinatario"
|
"no-peers_data-drop-bg": "Liberar para seleccionar destinatario",
|
||||||
|
"webrtc-requirement": "Para utilizar esta instancia de PairDrop, ¡WebRTC debe estar activado!"
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"processing": "Procesando…",
|
"processing": "Procesando…",
|
||||||
"click-to-send-paste-mode": "Haga clic para enviar {{descriptor}}",
|
"click-to-send-share-mode": "Haga clic para enviar {{descriptor}}",
|
||||||
"click-to-send": "Haga clic para enviar archivos o haga clic derecho para enviar un mensaje",
|
"click-to-send": "Haga clic para enviar archivos o haga clic derecho para enviar un mensaje",
|
||||||
"waiting": "Esperando…",
|
"waiting": "Esperando…",
|
||||||
"connection-hash": "Para verificar la seguridad del cifrado de extremo a extremo, compare este número de seguridad en ambos dispositivos",
|
"connection-hash": "Para verificar la seguridad del cifrado de extremo a extremo, compare este número de seguridad en ambos dispositivos",
|
||||||
|
@ -125,7 +126,7 @@
|
||||||
"title-image": "Imagen",
|
"title-image": "Imagen",
|
||||||
"file-other-description-file-plural": "y {{count}} archivos más",
|
"file-other-description-file-plural": "y {{count}} archivos más",
|
||||||
"would-like-to-share": "quisiera compartir",
|
"would-like-to-share": "quisiera compartir",
|
||||||
"send-message-to": "Enviar un Mensaje a",
|
"send-message-to": "Para:",
|
||||||
"language-selector-title": "Configurar Idioma",
|
"language-selector-title": "Configurar Idioma",
|
||||||
"pair": "Emparejar",
|
"pair": "Emparejar",
|
||||||
"hr-or": "O",
|
"hr-or": "O",
|
||||||
|
@ -144,7 +145,8 @@
|
||||||
"enter-room-id-from-another-device": "Ingresa el ID de la sala desde otro dispositivo para unirte a la sala.",
|
"enter-room-id-from-another-device": "Ingresa el ID de la sala desde otro dispositivo para unirte a la sala.",
|
||||||
"message_title": "Insertar el mensaje a enviar",
|
"message_title": "Insertar el mensaje a enviar",
|
||||||
"pair-devices-qr-code_title": "Haz clic para copiar el enlace para emparejar este dispositivo",
|
"pair-devices-qr-code_title": "Haz clic para copiar el enlace para emparejar este dispositivo",
|
||||||
"public-room-qr-code_title": "Haz clic para copiar el enlace a la sala pública"
|
"public-room-qr-code_title": "Haz clic para copiar el enlace a la sala pública",
|
||||||
|
"message_placeholder": "Texto"
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"claim": "La forma más sencilla de transferir archivos entre dispositivos",
|
"claim": "La forma más sencilla de transferir archivos entre dispositivos",
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"pair-device_title": "Associez vos appareils de manière permanente",
|
"pair-device_title": "Associez vos appareils de manière permanente",
|
||||||
"edit-paired-devices_title": "Gérer les appareils couplés",
|
"edit-paired-devices_title": "Gérer les appareils couplés",
|
||||||
"join-public-room_title": "Rejoindre temporairement la salle publique",
|
"join-public-room_title": "Rejoindre temporairement la salle publique",
|
||||||
"cancel-paste-mode": "Terminé"
|
"cancel-share-mode": "Terminé"
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"no-peers_data-drop-bg": "Déposer pour choisir le destinataire",
|
"no-peers_data-drop-bg": "Déposer pour choisir le destinataire",
|
||||||
|
@ -21,11 +21,11 @@
|
||||||
"x-instructions_mobile": "Appuyez pour envoyer des fichiers ou appuyez longuement pour envoyer un message",
|
"x-instructions_mobile": "Appuyez pour envoyer des fichiers ou appuyez longuement pour envoyer un message",
|
||||||
"x-instructions_data-drop-peer": "Déposer pour envoyer au destinataire",
|
"x-instructions_data-drop-peer": "Déposer pour envoyer au destinataire",
|
||||||
"x-instructions_data-drop-bg": "Lâcher pour choisir le destinataire",
|
"x-instructions_data-drop-bg": "Lâcher pour choisir le destinataire",
|
||||||
"click-to-send": "Cliquez pour envoyer",
|
"x-instructions-share-mode_desktop": "Cliquez pour envoyer",
|
||||||
"tap-to-send": "Appuyez pour envoyer",
|
"x-instructions-share-mode_mobile": "Appuyez pour envoyer",
|
||||||
"activate-paste-mode-base": "Ouvrez PairDrop sur d'autres appareils pour envoyer",
|
"activate-share-mode-base": "Ouvrez PairDrop sur d'autres appareils pour envoyer",
|
||||||
"activate-paste-mode-and-other-files": "et {{count}} autres fichiers",
|
"activate-share-mode-and-other-files-plural": "et {{count}} autres fichiers",
|
||||||
"activate-paste-mode-shared-text": "texte partagé"
|
"activate-share-mode-shared-text": "texte partagé"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"known-as": "Vous êtes connu comme :",
|
"known-as": "Vous êtes connu comme :",
|
||||||
|
@ -90,35 +90,38 @@
|
||||||
"receive-title": "{{descriptor}} Reçu",
|
"receive-title": "{{descriptor}} Reçu",
|
||||||
"download-again": "Télécharger à nouveau",
|
"download-again": "Télécharger à nouveau",
|
||||||
"language-selector-title": "Définir la langue",
|
"language-selector-title": "Définir la langue",
|
||||||
"system-language": "Langue du système"
|
"system-language": "Langue du système",
|
||||||
|
"message_title": "Insérer un message à envoyer",
|
||||||
|
"pair-devices-qr-code_title": "Cliquer pour copier pour appairer l'appareil",
|
||||||
|
"public-room-qr-code_title": "Cliquez pour copier le lien vers le salon public"
|
||||||
},
|
},
|
||||||
"about": {
|
"about": {
|
||||||
"close-about_aria-label": "Fermer à propos de PairDrop",
|
"close-about_aria-label": "Fermer à propos de PairDrop",
|
||||||
"claim": "Le moyen le plus simple de transférer des fichiers entre appareils",
|
"claim": "Le moyen le plus simple de transférer des fichiers entre appareils",
|
||||||
"github_title": "PairDrop sur GitHub",
|
"github_title": "PairDrop sur GitHub",
|
||||||
"buy-me-a-coffee_title": "Acheté-moi un café !",
|
"buy-me-a-coffee_title": "Achetez-moi un café !",
|
||||||
"tweet_title": "Tweet à propos de PairDrop",
|
"tweet_title": "Tweet à propos de PairDrop",
|
||||||
"faq_title": "Questions fréquemment posées"
|
"faq_title": "Questions fréquemment posées"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"display-name-changed-permanently": "Le nom d'affichage est modifié de manière permanente.",
|
"display-name-changed-permanently": "Le nom d'affichage est modifié de manière permanente",
|
||||||
"display-name-changed-temporarily": "Le nom d'affichage est modifié uniquement pour cette session.",
|
"display-name-changed-temporarily": "Le nom d'affichage est modifié uniquement pour cette session",
|
||||||
"display-name-random-again": "Le nom d'affichage est à nouveau généré aléatoirement.",
|
"display-name-random-again": "Le nom d'affichage est à nouveau généré aléatoirement",
|
||||||
"download-successful": "{{descriptor}} téléchargé",
|
"download-successful": "{{descriptor}} téléchargé",
|
||||||
"pairing-tabs-error": "Le couplage de deux onglets de navigateur Web est impossible.",
|
"pairing-tabs-error": "Le couplage de deux onglets de navigateur Web est impossible",
|
||||||
"pairing-success": "Appareils couplés.",
|
"pairing-success": "Appareils couplés",
|
||||||
"pairing-not-persistent": "Les appareils couplés ne sont pas persistants.",
|
"pairing-not-persistent": "Les appareils couplés ne sont pas persistants",
|
||||||
"pairing-key-invalid": "Clé invalide",
|
"pairing-key-invalid": "Clé invalide",
|
||||||
"pairing-key-invalidated": "Clé {{key}} invalidée.",
|
"pairing-key-invalidated": "Clé {{key}} invalidée",
|
||||||
"pairing-cleared": "Tous les appareils ne sont plus appairés.",
|
"pairing-cleared": "Tous les appareils ne sont plus appairés",
|
||||||
"public-room-id-invalid": "ID de salle non valide",
|
"public-room-id-invalid": "ID de salle non valide",
|
||||||
"public-room-left": "Salle publique {{publicRoomId}} quittée",
|
"public-room-left": "Salle publique {{publicRoomId}} quittée",
|
||||||
"copied-to-clipboard": "Copié dans le presse-papier",
|
"copied-to-clipboard": "Copié dans le presse-papier",
|
||||||
"copied-to-clipboard-error": "Copie impossible. Copier manuellement.",
|
"copied-to-clipboard-error": "Copie impossible. Copier manuellement.",
|
||||||
"text-content-incorrect": "Le contenu du texte est incorrect.",
|
"text-content-incorrect": "Le contenu du texte est incorrect",
|
||||||
"file-content-incorrect": "Le contenu du fichier est incorrect.",
|
"file-content-incorrect": "Le contenu du fichier est incorrect",
|
||||||
"clipboard-content-incorrect": "Le contenu du presse-papiers est incorrect.",
|
"clipboard-content-incorrect": "Le contenu du presse-papiers est incorrect",
|
||||||
"notifications-enabled": "Notifications activées.",
|
"notifications-enabled": "Notifications activées",
|
||||||
"link-received": "Lien reçu par {{name}} - Cliquez pour ouvrir",
|
"link-received": "Lien reçu par {{name}} - Cliquez pour ouvrir",
|
||||||
"message-received": "Message reçu par {{name}} - Cliquez pour copier",
|
"message-received": "Message reçu par {{name}} - Cliquez pour copier",
|
||||||
"click-to-download": "Cliquez pour télécharger",
|
"click-to-download": "Cliquez pour télécharger",
|
||||||
|
@ -128,17 +131,20 @@
|
||||||
"copied-text-error": "L'écriture dans le presse-papiers a échoué. Copiez manuellement !",
|
"copied-text-error": "L'écriture dans le presse-papiers a échoué. Copiez manuellement !",
|
||||||
"offline": "Vous êtes hors ligne",
|
"offline": "Vous êtes hors ligne",
|
||||||
"online": "Vous êtes de nouveau en ligne",
|
"online": "Vous êtes de nouveau en ligne",
|
||||||
"connected": "Connecté.",
|
"connected": "Connecté",
|
||||||
"online-requirement-pairing": "Vous devez être en ligne pour coupler des appareils.",
|
"online-requirement-pairing": "Vous devez être en ligne pour coupler des appareils",
|
||||||
"online-requirement-public-room": "Vous devez être en ligne pour créer une salle publique.",
|
"online-requirement-public-room": "Vous devez être en ligne pour créer une salle publique",
|
||||||
"connecting": "Connexion…",
|
"connecting": "Connexion…",
|
||||||
"files-incorrect": "Les fichiers sont incorrects.",
|
"files-incorrect": "Les fichiers sont incorrects",
|
||||||
"file-transfer-completed": "Transfert de fichier terminé.",
|
"file-transfer-completed": "Transfert de fichier terminé",
|
||||||
"ios-memory-limit": "L'envoi de fichiers vers iOS n'est possible que jusqu'à 200 Mo à la fois",
|
"ios-memory-limit": "L'envoi de fichiers vers iOS n'est possible que jusqu'à 200 Mo à la fois",
|
||||||
"message-transfer-completed": "Transfert de message terminé.",
|
"message-transfer-completed": "Transfert de message terminé",
|
||||||
"unfinished-transfers-warning": "Il y a des transferts inachevés. Êtes-vous sûr de vouloir fermer PairDrop ?",
|
"unfinished-transfers-warning": "Il y a des transferts inachevés. Êtes-vous sûr de vouloir fermer PairDrop ?",
|
||||||
"rate-limit-join-key": "Limite de débit atteinte. Attendez 10 secondes et réessayez.",
|
"rate-limit-join-key": "Limite de débit atteinte. Attendez 10 secondes et réessayez.",
|
||||||
"selected-peer-left": "Appareils sélectionnés restants."
|
"selected-peer-left": "Appareils sélectionnés restants",
|
||||||
|
"pair-url-copied-to-clipboard": "Lien de couplage de cet appareil copié dans le presse-papier",
|
||||||
|
"room-url-copied-to-clipboard": "Lien vers la salle publique copié dans le presse-papier",
|
||||||
|
"notifications-permissions-error": "Permission de notification bloquées car l'utilisateur a plusieurs fois rejeté la demande d'autorisation. Cela peut être réinitialisé via la Page d'Information en cliquant l’icône de cadenas à coté de l'URL."
|
||||||
},
|
},
|
||||||
"document-titles": {
|
"document-titles": {
|
||||||
"file-received": "Fichier reçu",
|
"file-received": "Fichier reçu",
|
||||||
|
@ -149,7 +155,7 @@
|
||||||
"message-received-plural": "{{count}} Messages reçus"
|
"message-received-plural": "{{count}} Messages reçus"
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"click-to-send-paste-mode": "Cliquez pour envoyer {{descriptor}}",
|
"click-to-send-share-mode": "Cliquez pour envoyer {{descriptor}}",
|
||||||
"click-to-send": "Cliquez pour envoyer des fichiers ou faites un clic droit pour envoyer un message",
|
"click-to-send": "Cliquez pour envoyer des fichiers ou faites un clic droit pour envoyer un message",
|
||||||
"connection-hash": "Pour vérifier la sécurité du chiffrement de bout en bout, comparez ce numéro de sécurité sur les deux appareils",
|
"connection-hash": "Pour vérifier la sécurité du chiffrement de bout en bout, comparez ce numéro de sécurité sur les deux appareils",
|
||||||
"preparing": "Préparation…",
|
"preparing": "Préparation…",
|
||||||
|
|
|
@ -20,36 +20,36 @@
|
||||||
"message-received": "Pesan diterima dari {{name}} - Klik untuk menyalin",
|
"message-received": "Pesan diterima dari {{name}} - Klik untuk menyalin",
|
||||||
"rate-limit-join-key": "Batasan tercapai. Tunggu 10 detik dan coba lagi.",
|
"rate-limit-join-key": "Batasan tercapai. Tunggu 10 detik dan coba lagi.",
|
||||||
"connecting": "Menghubungkan…",
|
"connecting": "Menghubungkan…",
|
||||||
"pairing-key-invalidated": "Kunci {{key}} tidak valid.",
|
"pairing-key-invalidated": "Kunci {{key}} tidak valid",
|
||||||
"pairing-key-invalid": "Kunci tidak valid",
|
"pairing-key-invalid": "Kunci tidak valid",
|
||||||
"connected": "Tersambung.",
|
"connected": "Tersambung",
|
||||||
"pairing-not-persistent": "Perangkat dipasangkan tidak akan bertahan lama.",
|
"pairing-not-persistent": "Perangkat dipasangkan tidak akan bertahan lama",
|
||||||
"text-content-incorrect": "Isi teks keliru.",
|
"text-content-incorrect": "Isi teks keliru",
|
||||||
"message-transfer-completed": "Transfer pesan selesai.",
|
"message-transfer-completed": "Transfer pesan selesai",
|
||||||
"file-transfer-completed": "Transfer file selesai.",
|
"file-transfer-completed": "Transfer file selesai",
|
||||||
"file-content-incorrect": "Isi file keliru.",
|
"file-content-incorrect": "Isi file keliru",
|
||||||
"files-incorrect": "File tidak benar.",
|
"files-incorrect": "File tidak benar",
|
||||||
"selected-peer-left": "Rekan terpilih keluar.",
|
"selected-peer-left": "Rekan terpilih keluar",
|
||||||
"link-received": "Tautan diterima dari {{name}} - Klik untuk membuka",
|
"link-received": "Tautan diterima dari {{name}} - Klik untuk membuka",
|
||||||
"online": "Anda kembali online",
|
"online": "Anda kembali online",
|
||||||
"public-room-left": "Keluar dari ruang publik {{publicRoomId}}",
|
"public-room-left": "Keluar dari ruang publik {{publicRoomId}}",
|
||||||
"copied-text": "Teks disalin ke papan klip",
|
"copied-text": "Teks disalin ke papan klip",
|
||||||
"display-name-random-again": "Nama tampilan dibuat secara acak lagi.",
|
"display-name-random-again": "Nama tampilan dibuat secara acak lagi",
|
||||||
"display-name-changed-permanently": "Nama tampilan diubah secara permanen.",
|
"display-name-changed-permanently": "Nama tampilan diubah secara permanen",
|
||||||
"copied-to-clipboard-error": "Penyalinan tak dapat dilakukan. Salinlah secara manual.",
|
"copied-to-clipboard-error": "Penyalinan tak dapat dilakukan. Salinlah secara manual.",
|
||||||
"pairing-success": "Perangkat dipasangkan.",
|
"pairing-success": "Perangkat dipasangkan",
|
||||||
"clipboard-content-incorrect": "Isi papan klip keliru.",
|
"clipboard-content-incorrect": "Isi papan klip keliru",
|
||||||
"display-name-changed-temporarily": "Nama tampilan hanya diubah untuk sesi ini.",
|
"display-name-changed-temporarily": "Nama tampilan hanya diubah untuk sesi ini",
|
||||||
"copied-to-clipboard": "Disalin ke papan klip",
|
"copied-to-clipboard": "Disalin ke papan klip",
|
||||||
"offline": "Anda sedang offline",
|
"offline": "Anda sedang offline",
|
||||||
"pairing-tabs-error": "Memasangkan dua tab browser web tidak mungkin dilakukan.",
|
"pairing-tabs-error": "Memasangkan dua tab browser web tidak mungkin dilakukan",
|
||||||
"public-room-id-invalid": "Room ID tidak valid",
|
"public-room-id-invalid": "Room ID tidak valid",
|
||||||
"click-to-download": "Klik untuk mengunduh",
|
"click-to-download": "Klik untuk mengunduh",
|
||||||
"pairing-cleared": "Semua Perangkat dilepaskan.",
|
"pairing-cleared": "Semua Perangkat dilepaskan",
|
||||||
"notifications-enabled": "Notifikasi diaktifkan.",
|
"notifications-enabled": "Notifikasi diaktifkan",
|
||||||
"online-requirement-pairing": "Anda harus online untuk memasangkan perangkat.",
|
"online-requirement-pairing": "Anda harus online untuk memasangkan perangkat",
|
||||||
"ios-memory-limit": "Mengirim file ke iOS hanya dapat dilakukan hingga 200 MB sekaligus",
|
"ios-memory-limit": "Mengirim file ke iOS hanya dapat dilakukan hingga 200 MB sekaligus",
|
||||||
"online-requirement-public-room": "Anda harus online untuk membuat ruang publik.",
|
"online-requirement-public-room": "Anda harus online untuk membuat ruang publik",
|
||||||
"copied-text-error": "Menyalin ke papan klip gagal. Salinlah secara manual!",
|
"copied-text-error": "Menyalin ke papan klip gagal. Salinlah secara manual!",
|
||||||
"download-successful": "{{descriptor}} diunduh",
|
"download-successful": "{{descriptor}} diunduh",
|
||||||
"click-to-show": "Klik untuk menampilkan",
|
"click-to-show": "Klik untuk menampilkan",
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
"room-url-copied-to-clipboard": "Tautan ke ruang publik disalin ke papan klip"
|
"room-url-copied-to-clipboard": "Tautan ke ruang publik disalin ke papan klip"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"cancel-paste-mode": "Selesai",
|
"cancel-share-mode": "Selesai",
|
||||||
"theme-auto_title": "Sesuaikan tema dengan sistem",
|
"theme-auto_title": "Sesuaikan tema dengan sistem",
|
||||||
"install_title": "Instal PairDrop",
|
"install_title": "Instal PairDrop",
|
||||||
"theme-dark_title": "Selalu gunakan tema gelap",
|
"theme-dark_title": "Selalu gunakan tema gelap",
|
||||||
|
@ -73,12 +73,12 @@
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_mobile": "Ketuk untuk mengirim file atau ketuk lama untuk mengirim pesan",
|
"x-instructions_mobile": "Ketuk untuk mengirim file atau ketuk lama untuk mengirim pesan",
|
||||||
"click-to-send": "Klik untuk mengirim",
|
"x-instructions-share-mode_desktop": "Klik untuk mengirim",
|
||||||
"activate-paste-mode-and-other-files": "dan {{count}} file lainnya",
|
"activate-share-mode-and-other-files-plural": "dan {{count}} file lainnya",
|
||||||
"tap-to-send": "Ketuk untuk mengirim",
|
"x-instructions-share-mode_mobile": "Ketuk untuk mengirim",
|
||||||
"activate-paste-mode-base": "Buka PairDrop di perangkat lain untuk berkirim",
|
"activate-share-mode-base": "Buka PairDrop di perangkat lain untuk berkirim",
|
||||||
"no-peers-subtitle": "Pasangkan perangkat atau masuk ke ruang publik agar dapat terdeteksi di jaringan lain",
|
"no-peers-subtitle": "Pasangkan perangkat atau masuk ke ruang publik agar dapat terdeteksi di jaringan lain",
|
||||||
"activate-paste-mode-shared-text": "teks bersama",
|
"activate-share-mode-shared-text": "teks bersama",
|
||||||
"x-instructions_desktop": "Klik untuk mengirim file atau klik kanan untuk mengirim pesan",
|
"x-instructions_desktop": "Klik untuk mengirim file atau klik kanan untuk mengirim pesan",
|
||||||
"no-peers-title": "Buka PairDrop di perangkat lain untuk berkirim file",
|
"no-peers-title": "Buka PairDrop di perangkat lain untuk berkirim file",
|
||||||
"x-instructions_data-drop-peer": "Lepaskan untuk mengirim ke rekan",
|
"x-instructions_data-drop-peer": "Lepaskan untuk mengirim ke rekan",
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"processing": "Memproses…",
|
"processing": "Memproses…",
|
||||||
"click-to-send-paste-mode": "Klik untuk mengirim {{descriptor}}",
|
"click-to-send-share-mode": "Klik untuk mengirim {{descriptor}}",
|
||||||
"click-to-send": "Klik untuk mengirim file atau klik kanan untuk mengirim pesan",
|
"click-to-send": "Klik untuk mengirim file atau klik kanan untuk mengirim pesan",
|
||||||
"waiting": "Menunggu…",
|
"waiting": "Menunggu…",
|
||||||
"connection-hash": "Untuk memverifikasi keamanan enkripsi end-to-end, bandingkan nomor keamanan ini pada kedua perangkat",
|
"connection-hash": "Untuk memverifikasi keamanan enkripsi end-to-end, bandingkan nomor keamanan ini pada kedua perangkat",
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"known-as": "Sei visibile come:"
|
"known-as": "Sei visibile come:"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"cancel-paste-mode": "Fatto",
|
"cancel-share-mode": "Fatto",
|
||||||
"theme-auto_title": "Adatta il tema al sistema automaticamente",
|
"theme-auto_title": "Adatta il tema al sistema automaticamente",
|
||||||
"install_title": "Installa PairDrop",
|
"install_title": "Installa PairDrop",
|
||||||
"theme-dark_title": "Usa sempre il tema scuro",
|
"theme-dark_title": "Usa sempre il tema scuro",
|
||||||
|
@ -30,12 +30,12 @@
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_mobile": "Tocca per inviare file o tocco prolungato per inviare un messaggio",
|
"x-instructions_mobile": "Tocca per inviare file o tocco prolungato per inviare un messaggio",
|
||||||
"click-to-send": "Clicca per inviare",
|
"x-instructions-share-mode_desktop": "Clicca per inviare",
|
||||||
"activate-paste-mode-and-other-files": "e altri {{count}} files",
|
"activate-share-mode-and-other-files-plural": "e altri {{count}} files",
|
||||||
"tap-to-send": "Tocca per inviare",
|
"x-instructions-share-mode_mobile": "Tocca per inviare",
|
||||||
"activate-paste-mode-base": "Apri PairDrop su altri dispositivi per inviare",
|
"activate-share-mode-base": "Apri PairDrop su altri dispositivi per inviare",
|
||||||
"no-peers-subtitle": "Abbina dispositivi o entra in una stanza pubblica per essere rilevabile su altre reti",
|
"no-peers-subtitle": "Abbina dispositivi o entra in una stanza pubblica per essere rilevabile su altre reti",
|
||||||
"activate-paste-mode-shared-text": "testo condiviso",
|
"activate-share-mode-shared-text": "testo condiviso",
|
||||||
"x-instructions_desktop": "Clicca per inviare files o usa il tasto destro per inviare un messaggio",
|
"x-instructions_desktop": "Clicca per inviare files o usa il tasto destro per inviare un messaggio",
|
||||||
"no-peers-title": "Apri PairDrop su altri dispositivi per inviare files",
|
"no-peers-title": "Apri PairDrop su altri dispositivi per inviare files",
|
||||||
"x-instructions_data-drop-peer": "Rilascia per inviare al peer",
|
"x-instructions_data-drop-peer": "Rilascia per inviare al peer",
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
"title-file-plural": "Files",
|
"title-file-plural": "Files",
|
||||||
"send-message-title": "Invia Messaggio",
|
"send-message-title": "Invia Messaggio",
|
||||||
"file-other-description-image-plural": "e {{count}} altre immagini",
|
"file-other-description-image-plural": "e {{count}} altre immagini",
|
||||||
"message_title": "Inserisci il messaggio da inviare",
|
"message_title": "Inserire messaggio da inviare",
|
||||||
"pair-devices-qr-code_title": "Clicca per copiare il link di abbinamento di questo dispositivo",
|
"pair-devices-qr-code_title": "Clicca per copiare il link di abbinamento di questo dispositivo",
|
||||||
"public-room-qr-code_title": "Clicca per copirare il link della stanza pubblica"
|
"public-room-qr-code_title": "Clicca per copirare il link della stanza pubblica"
|
||||||
},
|
},
|
||||||
|
@ -100,36 +100,36 @@
|
||||||
"message-received": "Messaggio ricevuto da {{name}} - Clicca per copiare",
|
"message-received": "Messaggio ricevuto da {{name}} - Clicca per copiare",
|
||||||
"rate-limit-join-key": "Limite raggiunto. Aspetta 10 secondi e riprova.",
|
"rate-limit-join-key": "Limite raggiunto. Aspetta 10 secondi e riprova.",
|
||||||
"connecting": "Connessione…",
|
"connecting": "Connessione…",
|
||||||
"pairing-key-invalidated": "Il codice {{key}} è stato invalidato.",
|
"pairing-key-invalidated": "Il codice {{key}} è stato invalidato",
|
||||||
"pairing-key-invalid": "Codice non valido",
|
"pairing-key-invalid": "Codice non valido",
|
||||||
"connected": "Connesso.",
|
"connected": "Connesso",
|
||||||
"pairing-not-persistent": "I dispositivi abbinati non sono persistenti.",
|
"pairing-not-persistent": "I dispositivi abbinati non sono persistenti",
|
||||||
"text-content-incorrect": "Il contenuto testuale non è corretto.",
|
"text-content-incorrect": "Il contenuto testuale non è corretto",
|
||||||
"message-transfer-completed": "Trasferimento del messaggio completato.",
|
"message-transfer-completed": "Trasferimento del messaggio completato",
|
||||||
"file-transfer-completed": "Trasferimento file completato.",
|
"file-transfer-completed": "Trasferimento file completato",
|
||||||
"file-content-incorrect": "Il contenuto del file non è corretto.",
|
"file-content-incorrect": "Il contenuto del file non è corretto",
|
||||||
"files-incorrect": "I file non sono corretti.",
|
"files-incorrect": "I file non sono corretti",
|
||||||
"selected-peer-left": "Peer selezionato ha abbandonato.",
|
"selected-peer-left": "Peer selezionato ha abbandonato",
|
||||||
"link-received": "Link ricevuto da {{name}} - Clicca per aprire",
|
"link-received": "Link ricevuto da {{name}} - Clicca per aprire",
|
||||||
"online": "Sei di nuovo online",
|
"online": "Sei di nuovo online",
|
||||||
"public-room-left": "Ha lasciato la stanza pubblica {{publicRoomId}}",
|
"public-room-left": "Ha lasciato la stanza pubblica {{publicRoomId}}",
|
||||||
"copied-text": "Testo copiato negli appunti",
|
"copied-text": "Testo copiato negli appunti",
|
||||||
"display-name-random-again": "Il nome visualizzato è generato casualmente un'altra volta.",
|
"display-name-random-again": "Il nome visualizzato è generato casualmente un'altra volta",
|
||||||
"display-name-changed-permanently": "Il nome visualizzato è cambiato permanentemente.",
|
"display-name-changed-permanently": "Il nome visualizzato è cambiato permanentemente",
|
||||||
"copied-to-clipboard-error": "La copia non è possibile. Copia manualmente.",
|
"copied-to-clipboard-error": "La copia non è possibile. Copia manualmente.",
|
||||||
"pairing-success": "Dispositivi abbinati.",
|
"pairing-success": "Dispositivi abbinati",
|
||||||
"clipboard-content-incorrect": "Il contenuto copiato non è corretto.",
|
"clipboard-content-incorrect": "Il contenuto copiato non è corretto",
|
||||||
"display-name-changed-temporarily": "Il nome visualizzato è cambiato solo per questa sessione.",
|
"display-name-changed-temporarily": "Il nome visualizzato è cambiato solo per questa sessione",
|
||||||
"copied-to-clipboard": "Copiato negli appunti",
|
"copied-to-clipboard": "Copiato negli appunti",
|
||||||
"offline": "Sei offline",
|
"offline": "Sei offline",
|
||||||
"pairing-tabs-error": "Abbinare due schede del browser è impossibile.",
|
"pairing-tabs-error": "Abbinare due schede del browser è impossibile",
|
||||||
"public-room-id-invalid": "ID stanza non valido",
|
"public-room-id-invalid": "ID stanza non valido",
|
||||||
"click-to-download": "Clicca per scaricare",
|
"click-to-download": "Clicca per scaricare",
|
||||||
"pairing-cleared": "Tutti i dispositivi sono stati dissociati.",
|
"pairing-cleared": "Tutti i dispositivi sono stati dissociati",
|
||||||
"notifications-enabled": "Notifiche attivate.",
|
"notifications-enabled": "Notifiche attivate",
|
||||||
"online-requirement-pairing": "Devi essere online per abbinare dispositivi.",
|
"online-requirement-pairing": "Devi essere online per abbinare dispositivi",
|
||||||
"ios-memory-limit": "L'invio di file a dispositivi iOS è possibile solo 200 MB alla volta",
|
"ios-memory-limit": "L'invio di file a dispositivi iOS è possibile solo 200 MB alla volta",
|
||||||
"online-requirement-public-room": "Devi essere online per creare una stanza pubblica.",
|
"online-requirement-public-room": "Devi essere online per creare una stanza pubblica",
|
||||||
"copied-text-error": "Scrittura negli appunti fallita. Copia manualmente!",
|
"copied-text-error": "Scrittura negli appunti fallita. Copia manualmente!",
|
||||||
"download-successful": "{{descriptor}} scaricato",
|
"download-successful": "{{descriptor}} scaricato",
|
||||||
"click-to-show": "Clicca per mostrare",
|
"click-to-show": "Clicca per mostrare",
|
||||||
|
@ -139,7 +139,7 @@
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"processing": "Elaborazione…",
|
"processing": "Elaborazione…",
|
||||||
"click-to-send-paste-mode": "Clicca per inviare {{descriptor}}",
|
"click-to-send-share-mode": "Clicca per inviare {{descriptor}}",
|
||||||
"click-to-send": "Clicca per inviare files o tasto destro per inviare un messaggio",
|
"click-to-send": "Clicca per inviare files o tasto destro per inviare un messaggio",
|
||||||
"waiting": "In attesa…",
|
"waiting": "In attesa…",
|
||||||
"connection-hash": "Per verificare la sicurezza della crittografia end-to-end, confronta questo numero di sicurezza su entrambi i dispositivi",
|
"connection-hash": "Per verificare la sicurezza della crittografia end-to-end, confronta questo numero di sicurezza su entrambi i dispositivi",
|
||||||
|
|
|
@ -20,36 +20,36 @@
|
||||||
"message-received": "{{name}}から受信したメッセージ(クリックしてコピー)",
|
"message-received": "{{name}}から受信したメッセージ(クリックしてコピー)",
|
||||||
"rate-limit-join-key": "レート制限に到達しました。10秒待ってから再度お試しください。",
|
"rate-limit-join-key": "レート制限に到達しました。10秒待ってから再度お試しください。",
|
||||||
"connecting": "接続中…",
|
"connecting": "接続中…",
|
||||||
"pairing-key-invalidated": "コード{{key}}が失効しました。",
|
"pairing-key-invalidated": "コード{{key}}が失効しました",
|
||||||
"pairing-key-invalid": "無効なコード",
|
"pairing-key-invalid": "無効なコード",
|
||||||
"connected": "接続しました。",
|
"connected": "接続しました",
|
||||||
"pairing-not-persistent": "ペア設定されたデバイスは永続化されていません。",
|
"pairing-not-persistent": "ペア設定されたデバイスは永続化されていません",
|
||||||
"text-content-incorrect": "テキストの内容が不正です。",
|
"text-content-incorrect": "テキストの内容が不正です",
|
||||||
"message-transfer-completed": "メッセージの送信が完了しました。",
|
"message-transfer-completed": "メッセージの送信が完了しました",
|
||||||
"file-transfer-completed": "ファイルの転送が完了しました。",
|
"file-transfer-completed": "ファイルの転送が完了しました",
|
||||||
"file-content-incorrect": "ファイルの内容が不正です。",
|
"file-content-incorrect": "ファイルの内容が不正です",
|
||||||
"files-incorrect": "ファイルが間違っています。",
|
"files-incorrect": "ファイルが間違っています",
|
||||||
"selected-peer-left": "選択した相手が退出しました。",
|
"selected-peer-left": "選択した相手が退出しました",
|
||||||
"link-received": "{{name}}から受信したリンク(クリックして開く)",
|
"link-received": "{{name}}から受信したリンク(クリックして開く)",
|
||||||
"online": "オンラインに復帰しました",
|
"online": "オンラインに復帰しました",
|
||||||
"public-room-left": "パブリックルーム{{publicRoomId}}から退出しました",
|
"public-room-left": "パブリックルーム{{publicRoomId}}から退出しました",
|
||||||
"copied-text": "テキストをクリップボードにコピーしました",
|
"copied-text": "テキストをクリップボードにコピーしました",
|
||||||
"display-name-random-again": "表示名がもう一度ランダムに生成されました。",
|
"display-name-random-again": "表示名がもう一度ランダムに生成されました",
|
||||||
"display-name-changed-permanently": "永続的な表示名が変更されました。",
|
"display-name-changed-permanently": "永続的な表示名が変更されました",
|
||||||
"copied-to-clipboard-error": "コピーできませんでした。手動でコピーしてください。",
|
"copied-to-clipboard-error": "コピーできませんでした。手動でコピーしてください。",
|
||||||
"pairing-success": "デバイスがペア設定されました。",
|
"pairing-success": "デバイスがペア設定されました",
|
||||||
"clipboard-content-incorrect": "クリップボードの内容が不正です。",
|
"clipboard-content-incorrect": "クリップボードの内容が不正です",
|
||||||
"display-name-changed-temporarily": "このセッションでの表示名が変更されました。",
|
"display-name-changed-temporarily": "このセッションでの表示名が変更されました",
|
||||||
"copied-to-clipboard": "クリップボードにコピーしました",
|
"copied-to-clipboard": "クリップボードにコピーしました",
|
||||||
"offline": "オフラインです",
|
"offline": "オフラインです",
|
||||||
"pairing-tabs-error": "同じWebブラウザーの2つのタブをペア設定することはできません。",
|
"pairing-tabs-error": "同じWebブラウザーの2つのタブをペア設定することはできません",
|
||||||
"public-room-id-invalid": "無効なルームID",
|
"public-room-id-invalid": "無効なルームID",
|
||||||
"click-to-download": "クリックしてダウンロード",
|
"click-to-download": "クリックしてダウンロード",
|
||||||
"pairing-cleared": "全てのデバイスのペア設定を解除しました。",
|
"pairing-cleared": "全てのデバイスのペア設定を解除しました",
|
||||||
"notifications-enabled": "通知が有効です。",
|
"notifications-enabled": "通知が有効です",
|
||||||
"online-requirement-pairing": "デバイスをペア設定するにはオンラインである必要があります。",
|
"online-requirement-pairing": "デバイスをペア設定するにはオンラインである必要があります",
|
||||||
"ios-memory-limit": "iOSへのファイル送信は一度に200MBまでしかできません",
|
"ios-memory-limit": "iOSへのファイル送信は一度に200MBまでしかできません",
|
||||||
"online-requirement-public-room": "パブリックルームを作成するにはオンラインである必要があります。",
|
"online-requirement-public-room": "パブリックルームを作成するにはオンラインである必要があります",
|
||||||
"copied-text-error": "クリップボードにコピーできませんでした。手動でコピーしてください。",
|
"copied-text-error": "クリップボードにコピーできませんでした。手動でコピーしてください。",
|
||||||
"download-successful": "{{descriptor}}をダウンロードしました",
|
"download-successful": "{{descriptor}}をダウンロードしました",
|
||||||
"click-to-show": "クリックして表示",
|
"click-to-show": "クリックして表示",
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
"room-url-copied-to-clipboard": "パブリックルームへのリンクをクリップボードにコピーしました"
|
"room-url-copied-to-clipboard": "パブリックルームへのリンクをクリップボードにコピーしました"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"cancel-paste-mode": "完了",
|
"cancel-share-mode": "完了",
|
||||||
"theme-auto_title": "テーマをシステムの設定に自動的に合わせる",
|
"theme-auto_title": "テーマをシステムの設定に自動的に合わせる",
|
||||||
"install_title": "PairDropをインストール",
|
"install_title": "PairDropをインストール",
|
||||||
"theme-dark_title": "常にダークテーマを使用する",
|
"theme-dark_title": "常にダークテーマを使用する",
|
||||||
|
@ -73,12 +73,12 @@
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_mobile": "タップしてファイルを送信または長押ししてメッセージを送信します",
|
"x-instructions_mobile": "タップしてファイルを送信または長押ししてメッセージを送信します",
|
||||||
"click-to-send": "クリックして送信",
|
"x-instructions-share-mode_desktop": "クリックして送信",
|
||||||
"activate-paste-mode-and-other-files": "とその他{{count}}個のファイル",
|
"activate-share-mode-and-other-files-plural": "とその他{{count}}個のファイル",
|
||||||
"tap-to-send": "タップして送信",
|
"x-instructions-share-mode_mobile": "タップして送信",
|
||||||
"activate-paste-mode-base": "他のデバイスでPairDropを開いて送信します",
|
"activate-share-mode-base": "他のデバイスでPairDropを開いて送信します",
|
||||||
"no-peers-subtitle": "デバイスをペア設定するかパブリックルームに参加すると、他のネットワーク上からも見つけられるようになります",
|
"no-peers-subtitle": "デバイスをペア設定するかパブリックルームに参加すると、他のネットワーク上からも見つけられるようになります",
|
||||||
"activate-paste-mode-shared-text": "共有されたテキスト",
|
"activate-share-mode-shared-text": "共有されたテキスト",
|
||||||
"x-instructions_desktop": "左クリックしてファイルを送信または右クリックしてメッセージを送信します",
|
"x-instructions_desktop": "左クリックしてファイルを送信または右クリックしてメッセージを送信します",
|
||||||
"no-peers-title": "他のデバイスでPairDropを開いてファイルを送信します",
|
"no-peers-title": "他のデバイスでPairDropを開いてファイルを送信します",
|
||||||
"x-instructions_data-drop-peer": "離すとこの相手に送信します",
|
"x-instructions_data-drop-peer": "離すとこの相手に送信します",
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"processing": "処理中…",
|
"processing": "処理中…",
|
||||||
"click-to-send-paste-mode": "クリックして{{descriptor}}を送信",
|
"click-to-send-share-mode": "クリックして{{descriptor}}を送信",
|
||||||
"click-to-send": "クリックしてファイルを送信または右クリックしてメッセージを送信します",
|
"click-to-send": "クリックしてファイルを送信または右クリックしてメッセージを送信します",
|
||||||
"waiting": "待機中…",
|
"waiting": "待機中…",
|
||||||
"connection-hash": "エンドツーエンド暗号化のセキュリティを確認するには、両方のデバイスのセキュリティナンバーを確認します",
|
"connection-hash": "エンドツーエンド暗号化のセキュリティを確認するには、両方のデバイスのセキュリティナンバーを確認します",
|
||||||
|
|
167
public/lang/kn.json
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"about_title": "PairDrop ಕುರಿತು",
|
||||||
|
"cancel-paste-mode": "ಮಾಡಿದ",
|
||||||
|
"theme-auto_title": "ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಿಸ್ಟಮ್ಗೆ ಥೀಮ್ ಅನ್ನು ಹೊಂದಿಸಿ",
|
||||||
|
"install_title": "PairDrop ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ",
|
||||||
|
"theme-dark_title": "ಯಾವಾಗಲೂ ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಬಳಸಿ",
|
||||||
|
"pair-device_title": "ನಿಮ್ಮ ಸಾಧನಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ಜೋಡಿ ಮಾಡಿ",
|
||||||
|
"join-public-room_title": "ತಾತ್ಕಾಲಿಕವಾಗಿ ಸಾರ್ವಜನಿಕ ಕೊಠಡಿಯನ್ನು ಸೇರಿರಿ",
|
||||||
|
"notification_title": "ಸೂಚನೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ",
|
||||||
|
"edit-paired-devices_title": "ಜೋಡಿಯಾಗಿರುವ ಸಾಧನಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ",
|
||||||
|
"language-selector_title": "ಭಾಷೆಯನ್ನು ಆಯ್ಕೆ ಮಾಡಿ",
|
||||||
|
"about_aria-label": "PairDrop ಕುರಿತು ಪುಟವನ್ನು ತೆರೆಯಿರಿ",
|
||||||
|
"theme-light_title": "ಯಾವಾಗಲೂ ಲೈಟ್ ಥೀಮ್ ಅನ್ನು ಬಳಸಿ"
|
||||||
|
},
|
||||||
|
"dialogs": {
|
||||||
|
"message_placeholder": "ಪಠ್ಯ",
|
||||||
|
"base64-paste-to-send": "{{type}} ಕಳುಹಿಸಲು ಇಲ್ಲಿ ಅಂಟಿಸಿ",
|
||||||
|
"auto-accept-instructions-2": "ಆ ಸಾಧನದಿಂದ ಕಳುಹಿಸಲಾದ ಎಲ್ಲಾ ಫೈಲ್ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸ್ವೀಕರಿಸಲು.",
|
||||||
|
"receive-text-title": "ಸಂದೇಶವನ್ನು ಸ್ವೀಕರಿಸಲಾಗಿದೆ",
|
||||||
|
"edit-paired-devices-title": "ಜೋಡಿಯಾಗಿರುವ ಸಾಧನಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ",
|
||||||
|
"cancel": "ರದ್ದುಗೊಳಿಸಿ",
|
||||||
|
"auto-accept-instructions-1": "ಸಕ್ರಿಯಗೊಳಿಸಿ",
|
||||||
|
"pair-devices-title": "ಸಾಧನಗಳನ್ನು ಶಾಶ್ವತವಾಗಿ ಜೋಡಿಸಿ",
|
||||||
|
"download": "ಡೌನ್ಲೋಡ್ ಮಾಡಿ",
|
||||||
|
"title-file": "ಫೈಲ್",
|
||||||
|
"base64-processing": "ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗುತ್ತಿದೆ…",
|
||||||
|
"decline": "ನಿರಾಕರಿಸಿ",
|
||||||
|
"receive-title": "{{descriptor}} ಸ್ವೀಕರಿಸಲಾಗಿದೆ",
|
||||||
|
"leave": "ಬಿಡಿ",
|
||||||
|
"message_title": "ಕಳುಹಿಸಲು ಸಂದೇಶವನ್ನು ನಮೂದಿಸಿ",
|
||||||
|
"join": "ಸೇರಿಕೊಳ್ಳಿ",
|
||||||
|
"title-image-plural": "ಚಿತ್ರಗಳು",
|
||||||
|
"send": "ಕಳುಹಿಸಿ",
|
||||||
|
"base64-tap-to-paste": "{{type}} ಅಂಟಿಸಲು ಇಲ್ಲಿ ಟ್ಯಾಪ್ ಮಾಡಿ",
|
||||||
|
"base64-text": "ಪಠ್ಯ",
|
||||||
|
"copy": "ನಕಲು ಮಾಡಿ",
|
||||||
|
"file-other-description-image": "ಮತ್ತು ಇನ್ನೊಂದು ಚಿತ್ರ",
|
||||||
|
"pair-devices-qr-code_title": "ಈ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಬಳಸುವ ಲಿಂಕ್ ನಕಲಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"temporary-public-room-title": "ತಾತ್ಕಾಲಿಕ ಸಾರ್ವಜನಿಕ ಕೊಠಡಿ",
|
||||||
|
"base64-files": "ಫೈಲ್ಗಳು",
|
||||||
|
"has-sent": "ಕಳುಹಿಸಿದ್ದಾರೆ:",
|
||||||
|
"file-other-description-file": "ಮತ್ತು ಇನ್ನೊಂದು ಫೈಲ್",
|
||||||
|
"public-room-qr-code_title": "ಸಾರ್ವಜನಿಕ ಕೊಠಡಿಗೆ ಲಿಂಕ್ ಅನ್ನು ನಕಲಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"close": "ಮುಚ್ಚಿ",
|
||||||
|
"system-language": "ಸಿಸ್ಟಮ್ ಭಾಷೆ",
|
||||||
|
"unpair": "ಜೋಡಿಯನ್ನು ತೆಗೆಯಿರಿ",
|
||||||
|
"title-image": "ಚಿತ್ರ",
|
||||||
|
"file-other-description-file-plural": "ಮತ್ತು {{count}} ಇತರ ಫೈಲ್ಗಳು",
|
||||||
|
"would-like-to-share": "ಹಂಚಿಕೊಳ್ಳಲು ಬಯಸುತ್ತಾರೆ",
|
||||||
|
"send-message-to": "ಇವರಿಗೆ:",
|
||||||
|
"language-selector-title": "ಭಾಷೆಯನ್ನು ಹೊಂದಿಸಿ",
|
||||||
|
"pair": "ಜೋಡಿ",
|
||||||
|
"hr-or": "ಅಥವಾ",
|
||||||
|
"scan-qr-code": "ಅಥವಾ QR-ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿ.",
|
||||||
|
"input-key-on-this-device": "ಇನ್ನೊಂದು ಸಾಧನದಲ್ಲಿ ಈ ಕೀಲಿಯನ್ನು ನಮೂದಿಸಿ",
|
||||||
|
"download-again": "ಮತ್ತೊಮ್ಮೆ ಡೌನ್ಲೋಡ್ ಮಾಡಿ",
|
||||||
|
"accept": "ಒಪ್ಪಿಕೊಳ್ಳಿ",
|
||||||
|
"paired-devices-wrapper_data-empty": "ಜೋಡಿಯಾಗಿರುವ ಸಾಧನಗಳಿಲ್ಲ.",
|
||||||
|
"enter-key-from-another-device": "ಇನ್ನೊಂದು ಸಾಧನದಿಂದ ಕೀಲಿಯನ್ನು ಇಲ್ಲಿ ನಮೂದಿಸಿ.",
|
||||||
|
"share": "ಹಂಚಿಕೊಳ್ಳಿ",
|
||||||
|
"auto-accept": "ಸ್ವಯಂ ಸ್ವೀಕರಿಸು",
|
||||||
|
"title-file-plural": "ಫೈಲ್ಗಳು",
|
||||||
|
"send-message-title": "ಸಂದೇಶ ಕಳುಹಿಸಿ",
|
||||||
|
"input-room-id-on-another-device": "ಇನ್ನೊಂದು ಸಾಧನದಲ್ಲಿ ಈ ರೂಮ್ ಐಡಿಯನ್ನು ನಮೂದಿಸಿ",
|
||||||
|
"file-other-description-image-plural": "ಮತ್ತು {{count}} ಇತರ ಚಿತ್ರಗಳು",
|
||||||
|
"enter-room-id-from-another-device": "ಕೊಠಡಿ ಸೇರಲು ಇನ್ನೊಂದು ಸಾಧನದಿಂದ ರೂಮ್ ಐಡಿ ನಮೂದಿಸಿ."
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"webrtc": "WebRTC ಲಭ್ಯವಿಲ್ಲದಿದ್ದರೆ.",
|
||||||
|
"public-room-devices_title": "ನೆಟ್ವರ್ಕ್ನಿಂದ ಸ್ವತಂತ್ರವಾದ ಈ ಸಾರ್ವಜನಿಕ ಕೊಠಡಿಯಲ್ಲಿನ ಸಾಧನಗಳ ಮೂಲಕ ನಿಮ್ಮನ್ನು ಕಂಡುಹಿಡಿಯಬಹುದು.",
|
||||||
|
"display-name_data-placeholder": "ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ…",
|
||||||
|
"display-name_title": "ನಿಮ್ಮ ಸಾಧನದ ಹೆಸರನ್ನು ಶಾಶ್ವತವಾಗಿ ಎಡಿಟ್ ಮಾಡಿ",
|
||||||
|
"traffic": "ಟ್ರಾಫಿಕ್ ಅನ್ನು",
|
||||||
|
"paired-devices_title": "ನೆಟ್ವರ್ಕ್ನಿಂದ ಸ್ವತಂತ್ರವಾಗಿ ಎಲ್ಲಾ ಸಮಯದಲ್ಲೂ ಜೋಡಿಸಲಾದ ಸಾಧನಗಳಿಂದ ನಿಮ್ಮನ್ನು ಕಂಡುಹಿಡಿಯಬಹುದು.",
|
||||||
|
"public-room-devices": "{{roomId}} ಕೊಠಡಿಯಲ್ಲಿ",
|
||||||
|
"paired-devices": "ಜೋಡಿಸಲಾದ ಸಾಧನಗಳ ಮೂಲಕ",
|
||||||
|
"on-this-network": "ಈ ನೆಟ್ವರ್ಕ್ನಲ್ಲಿ",
|
||||||
|
"routed": "ಸರ್ವರ್ ಮೂಲಕ ರವಾನಿಸಲಾಗಿದೆ",
|
||||||
|
"discovery": "ನಿಮ್ಮನ್ನು ಕಂಡುಹಿಡಿಯಲಾಗುವುದು:",
|
||||||
|
"on-this-network_title": "ಈ ನೆಟ್ವರ್ಕ್ನಲ್ಲಿರುವ ಪ್ರತಿಯೊಬ್ಬರಿಂದ ನಿಮ್ಮನ್ನು ಕಂಡುಹಿಡಿಯಬಹುದು.",
|
||||||
|
"known-as": "ನಿಮ್ಮನ್ನು ಹೀಗೆ ಕರೆಯಲಾಗುತ್ತದೆ:"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"request-title": "{{name}} ಅವರು {{count}} {{descriptor}} ಅನ್ನು ವರ್ಗಾಯಿಸಲು ಬಯಸುತ್ತಾರೆ",
|
||||||
|
"unfinished-transfers-warning": "ಅಪೂರ್ಣ ವರ್ಗಾವಣೆಗಳಿವೆ. PairDrop ಅನ್ನು ಮುಚ್ಚಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?",
|
||||||
|
"message-received": "{{name}} ಅವರಿಂದ ಸಂದೇಶವನ್ನು ಸ್ವೀಕರಿಸಲಾಗಿದೆ - ನಕಲಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"notifications-permissions-error": "ಬಳಕೆದಾರರು ಹಲವಾರು ಬಾರಿ ಅನುಮತಿ ಪ್ರಾಂಪ್ಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿರುವುದರಿಂದ ಸೂಚನೆಗಳ ಅನುಮತಿಯನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ. ಇದನ್ನು ಪುಟ ಮಾಹಿತಿಯಲ್ಲಿ ಮರುಹೊಂದಿಸಬಹುದು, URL ಬಾರ್ನ ಪಕ್ಕದಲ್ಲಿರುವ ಬೀಗದ ಐಕಾನ್ ಕ್ಲಿಕ್ ಮಾಡುವ ಮೂಲಕ ಪ್ರವೇಶಿಸಬಹುದು.",
|
||||||
|
"rate-limit-join-key": "ದರದ ಮಿತಿ ತಲುಪಿದೆ. ೧೦ ಸೆಕೆಂಡುಗಳು ನಿರೀಕ್ಷಿಸಿ ಮತ್ತು ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.",
|
||||||
|
"pair-url-copied-to-clipboard": "ಈ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಬಳಸುವ ಲಿಂಕ್ ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಲಾಗಿದೆ",
|
||||||
|
"connecting": "ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ…",
|
||||||
|
"pairing-key-invalidated": "ಕೀಲಿ {{key}} ಅಮಾನ್ಯಗೊಂಡಿದೆ",
|
||||||
|
"pairing-key-invalid": "ಅಮಾನ್ಯವಾದ ಕೀಲಿ",
|
||||||
|
"connected": "ಸಂಪರ್ಕಿಸಲಾಗಿದೆ",
|
||||||
|
"pairing-not-persistent": "ಜೋಡಿಯಾಗಿರುವ ಸಾಧನಗಳು ನಿರಂತರವಾಗಿರುವುದಿಲ್ಲ",
|
||||||
|
"text-content-incorrect": "ಪಠ್ಯದ ವಿಷಯ ತಪ್ಪಾಗಿದೆ",
|
||||||
|
"message-transfer-completed": "ಸಂದೇಶ ವರ್ಗಾವಣೆ ಪೂರ್ಣಗೊಂಡಿದೆ",
|
||||||
|
"file-transfer-completed": "ಫೈಲ್ ವರ್ಗಾವಣೆ ಮುಗಿದಿದೆ",
|
||||||
|
"file-content-incorrect": "ಫೈಲ್ ವಿಷಯವು ತಪ್ಪಾಗಿದೆ",
|
||||||
|
"files-incorrect": "ಫೈಲ್ಗಳು ಸರಿಯಾಗಿಲ್ಲ",
|
||||||
|
"selected-peer-left": "ಆಯ್ದ ಪೀರ್ ತೊರೆದಿದ್ದಾರೆ",
|
||||||
|
"link-received": "{{name}} ಮೂಲಕ ಲಿಂಕ್ ಸ್ವೀಕರಿಸಲಾಗಿದೆ - ತೆರೆಯಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"online": "ನೀವು ಆನ್ಲೈನ್ ಮರಳಿದಿರಿ",
|
||||||
|
"public-room-left": "ಸಾರ್ವಜನಿಕ ಕೊಠಡಿ {{publicRoomId}} ಯನ್ನು ತೊರೆದಿರುವಿರಿ",
|
||||||
|
"copied-text": "ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ಪಠ್ಯವನ್ನು ನಕಲಿಸಲಾಗಿದೆ",
|
||||||
|
"display-name-random-again": "ಪ್ರದರ್ಶನದ ಹೆಸರನ್ನು ಯಾದೃಚ್ಛಿಕವಾಗಿ ಮತ್ತೆ ರಚಿಸಲಾಗಿದೆ",
|
||||||
|
"display-name-changed-permanently": "ಪ್ರದರ್ಶನದ ಹೆಸರನ್ನು ಶಾಶ್ವತವಾಗಿ ಬದಲಾಯಿಸಲಾಗಿದೆ",
|
||||||
|
"copied-to-clipboard-error": "ನಕಲು ಮಾಡುವುದು ಅಸಾಧ್ಯ. ಕೈಯಾರೆ ನಕಲಿಸಿ.",
|
||||||
|
"pairing-success": "ಸಾಧನಗಳನ್ನು ಜೋಡಿಸಲಾಗಿದೆ",
|
||||||
|
"clipboard-content-incorrect": "ಕ್ಲಿಪ್ಬೋರ್ಡ್ನ ವಿಷಯವು ತಪ್ಪಾಗಿದೆ",
|
||||||
|
"display-name-changed-temporarily": "ಪ್ರದರ್ಶನದ ಹೆಸರನ್ನು ಈ ಸೆಶನ್ಗೆ ಮಾತ್ರ ಬದಲಾಯಿಸಲಾಗಿದೆ",
|
||||||
|
"copied-to-clipboard": "ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಲಾಗಿದೆ",
|
||||||
|
"offline": "ನೀವು ಆಫ್ಲೈನ್ ಇದ್ದೀರಿ",
|
||||||
|
"pairing-tabs-error": "ಎರಡು ವೆಬ್ ಬ್ರೌಸರ್ ಟ್ಯಾಬ್ಗಳನ್ನು ಜೋಡಿಸುವುದು ಅಸಾಧ್ಯ",
|
||||||
|
"public-room-id-invalid": "ಅಮಾನ್ಯವಾದ ಕೊಠಡಿ ಐಡಿ",
|
||||||
|
"click-to-download": "ಡೌನ್ಲೋಡ್ ಮಾಡಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"pairing-cleared": "ಎಲ್ಲಾ ಸಾಧನಗಳನ್ನು ಜೋಡಿಯಾಗಿ ತೆಗೆಯಲಾಗಿದೆ",
|
||||||
|
"notifications-enabled": "ಸೂಚನೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ",
|
||||||
|
"online-requirement-pairing": "ಸಾಧನಗಳನ್ನು ಜೋಡಿಸಲು ನೀವು ಆನ್ಲೈನ್ ಇರಬೇಕು",
|
||||||
|
"ios-memory-limit": "iOSಗೆ ಫೈಲ್ಗಳನ್ನು ಕಳುಹಿಸುವುದು ಒಂದೇ ಬಾರಿಗೆ 200 MB ವರೆಗೆ ಮಾತ್ರ ಸಾಧ್ಯವಾಗಿದೆ",
|
||||||
|
"online-requirement-public-room": "ಸಾರ್ವಜನಿಕ ಕೊಠಡಿಯನ್ನು ರಚಿಸಲು ನೀವು ಆನ್ಲೈನ್ ಇರಬೇಕು",
|
||||||
|
"room-url-copied-to-clipboard": "ಸಾರ್ವಜನಿಕ ಕೊಠಡಿಯ ಲಿಂಕ್ ಅನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಲಾಗಿದೆ",
|
||||||
|
"copied-text-error": "ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ಬರೆಯುವುದು ವಿಫಲವಾಗಿದೆ. ಕೈಯಾರೆ ನಕಲಿಸಿ!",
|
||||||
|
"download-successful": "{{descriptor}} ಅನ್ನು ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗಿದೆ",
|
||||||
|
"click-to-show": "ತೋರಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"
|
||||||
|
},
|
||||||
|
"instructions": {
|
||||||
|
"x-instructions_mobile": "ಫೈಲ್ಗಳನ್ನು ಕಳುಹಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ ಅಥವಾ ಸಂದೇಶವನ್ನು ಕಳುಹಿಸಲು ದೀರ್ಘವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ",
|
||||||
|
"click-to-send": "ಕಳುಹಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"activate-paste-mode-and-other-files": "ಮತ್ತು ಇತರ {{count}} ಫೈಲ್ಗಳು",
|
||||||
|
"tap-to-send": "ಕಳುಹಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ",
|
||||||
|
"activate-paste-mode-base": "ಕಳುಹಿಸಲು ಇತರ ಸಾಧನಗಳಲ್ಲಿ PairDrop ತೆರೆಯಿರಿ",
|
||||||
|
"no-peers-subtitle": "ಇತರ ನೆಟ್ವರ್ಕ್ಗಳಲ್ಲಿ ಅನ್ವೇಷಿಸಲು ಸಾಧನಗಳನ್ನು ಜೋಡಿಸಿ ಅಥವಾ ಸಾರ್ವಜನಿಕ ಕೊಠಡಿಯನ್ನು ನಮೂದಿಸಿ",
|
||||||
|
"activate-paste-mode-shared-text": "ಹಂಚಿದ ಪಠ್ಯ",
|
||||||
|
"x-instructions_desktop": "ಫೈಲ್ಗಳನ್ನು ಕಳುಹಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ ಅಥವಾ ಸಂದೇಶ ಕಳುಹಿಸಲು ಬಲ ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"no-peers-title": "ಫೈಲ್ಗಳನ್ನು ಕಳುಹಿಸಲು PairDrop ಅನ್ನು ಇತರ ಸಾಧನಗಳಲ್ಲಿ ತೆರೆಯಿರಿ",
|
||||||
|
"x-instructions_data-drop-peer": "ಪೀರ್ಗೆ ಕಳುಹಿಸಲು ಬಿಡುಗಡೆ ಮಾಡಿ",
|
||||||
|
"x-instructions_data-drop-bg": "ಸ್ವೀಕರಿಸುವವರನ್ನು ಆಯ್ಕೆ ಮಾಡಲು ಬಿಡುಗಡೆ ಮಾಡಿ",
|
||||||
|
"no-peers_data-drop-bg": "ಸ್ವೀಕರಿಸುವವರನ್ನು ಆಯ್ಕೆ ಮಾಡಲು ಬಿಡುಗಡೆ ಮಾಡಿ",
|
||||||
|
"webrtc-requirement": "ಈ PairDrop ನಿದರ್ಶನವನ್ನು ಬಳಸಲು, WebRTC ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕು!"
|
||||||
|
},
|
||||||
|
"peer-ui": {
|
||||||
|
"processing": "ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗುತ್ತಿದೆ…",
|
||||||
|
"click-to-send-paste-mode": "{{descriptor}} ಕಳುಹಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"click-to-send": "ಫೈಲ್ಗಳನ್ನು ಕಳುಹಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ ಅಥವಾ ಸಂದೇಶ ಕಳುಹಿಸಲು ಬಲ ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||||
|
"waiting": "ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ…",
|
||||||
|
"connection-hash": "ಎಂಡ್-ಟು-ಎಂಡ್ ಎನ್ಕ್ರಿಪ್ಶನ್ನ ಭದ್ರತೆಯನ್ನು ಪರಿಶೀಲಿಸಲು, ಎರಡೂ ಸಾಧನಗಳಲ್ಲಿ ಈ ಭದ್ರತಾ ಸಂಖ್ಯೆಯನ್ನು ಹೋಲಿಸಿ",
|
||||||
|
"preparing": "ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ…",
|
||||||
|
"transferring": "ವರ್ಗಾಯಿಸಲಾಗುತ್ತಿದೆ…"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"claim": "ಸಾಧನಗಳಾದ್ಯಂತ ಫೈಲ್ಗಳನ್ನು ವರ್ಗಾಯಿಸಲು ಸುಲಭವಾದ ಮಾರ್ಗ",
|
||||||
|
"tweet_title": "PairDrop ಕುರಿತು ಟ್ವೀಟ್ ಮಾಡಿ",
|
||||||
|
"close-about_aria-label": "PairDrop ಕುರಿತು ಪುಟವನ್ನು ಮುಚ್ಚಿ",
|
||||||
|
"buy-me-a-coffee_title": "ನನಗೆ ಕಾಫಿ ಖರೀದಿಸಿ!",
|
||||||
|
"github_title": "GitHub ನಲ್ಲಿ PairDrop",
|
||||||
|
"faq_title": "ಪದೇ ಪದೇ ಕೇಳಲಾಗುವ ಪ್ರಶ್ನೆಗಳು"
|
||||||
|
},
|
||||||
|
"document-titles": {
|
||||||
|
"file-transfer-requested": "ಫೈಲ್ ವರ್ಗಾವಣೆಗೆ ವಿನಂತಿಸಲಾಗಿದೆ",
|
||||||
|
"image-transfer-requested": "ಚಿತ್ರದ ವರ್ಗಾವಣೆಯನ್ನು ವಿನಂತಿಸಲಾಗಿದೆ",
|
||||||
|
"message-received-plural": "{{count}} ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಲಾಗಿದೆ",
|
||||||
|
"message-received": "ಸಂದೇಶವನ್ನು ಸ್ವೀಕರಿಸಲಾಗಿದೆ",
|
||||||
|
"file-received": "ಫೈಲ್ ಸ್ವೀಕರಿಸಲಾಗಿದೆ",
|
||||||
|
"file-received-plural": "{{count}} ಫೈಲ್ಗಳನ್ನು ಸ್ವೀಕರಿಸಲಾಗಿದೆ"
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,10 +6,11 @@
|
||||||
"theme-auto_title": "Juster drakt til system",
|
"theme-auto_title": "Juster drakt til system",
|
||||||
"theme-light_title": "Alltid bruk lys drakt",
|
"theme-light_title": "Alltid bruk lys drakt",
|
||||||
"theme-dark_title": "Alltid bruk mørk drakt",
|
"theme-dark_title": "Alltid bruk mørk drakt",
|
||||||
"notification_title": "Skru på merknader",
|
"notification_title": "Skru på varslinger",
|
||||||
"cancel-paste-mode": "Ferdig",
|
"cancel-share-mode": "Ferdig",
|
||||||
"install_title": "Installer PairDrop",
|
"install_title": "Installer PairDrop",
|
||||||
"pair-device_title": "Sammenkoble enhet"
|
"pair-device_title": "Sammenkoble enhet",
|
||||||
|
"language-selector_title": "Velg språk"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"webrtc": "hvis WebRTC ikke er tilgjengelig.",
|
"webrtc": "hvis WebRTC ikke er tilgjengelig.",
|
||||||
|
@ -25,15 +26,15 @@
|
||||||
"x-instructions_desktop": "Klikk for å sende filer, eller høyreklikk for å sende en melding",
|
"x-instructions_desktop": "Klikk for å sende filer, eller høyreklikk for å sende en melding",
|
||||||
"x-instructions_mobile": "Trykk for å sende filer, eller lang-trykk for å sende en melding",
|
"x-instructions_mobile": "Trykk for å sende filer, eller lang-trykk for å sende en melding",
|
||||||
"x-instructions_data-drop-bg": "Slipp for å velge mottager",
|
"x-instructions_data-drop-bg": "Slipp for å velge mottager",
|
||||||
"click-to-send": "Klikk for å sende",
|
"x-instructions-share-mode_desktop": "Klikk for å sende",
|
||||||
"no-peers_data-drop-bg": "Slipp for å velge mottager",
|
"no-peers_data-drop-bg": "Slipp for å velge mottager",
|
||||||
"no-peers-title": "Åpne PairDrop på andre enheter for å sende filer",
|
"no-peers-title": "Åpne PairDrop på andre enheter for å sende filer",
|
||||||
"no-peers-subtitle": "Sammenkoble enheter for å kunne oppdages på andre nettverk",
|
"no-peers-subtitle": "Sammenkoble enheter for å kunne oppdages på andre nettverk",
|
||||||
"x-instructions_data-drop-peer": "Slipp for å sende til likemann",
|
"x-instructions_data-drop-peer": "Slipp for å sende til likemann",
|
||||||
"tap-to-send": "Trykk for å sende",
|
"x-instructions-share-mode_mobile": "Trykk for å sende",
|
||||||
"activate-paste-mode-base": "Åpne PairDrop på andre enheter for å sende",
|
"activate-share-mode-base": "Åpne PairDrop på andre enheter for å sende",
|
||||||
"activate-paste-mode-and-other-files": "og {{count}} andre filer",
|
"activate-share-mode-and-other-files-plural": "og {{count}} andre filer",
|
||||||
"activate-paste-mode-shared-text": "delt tekst"
|
"activate-share-mode-shared-text": "delt tekst"
|
||||||
},
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
"input-key-on-this-device": "Skriv inn denne nøkkelen på en annen enhet",
|
"input-key-on-this-device": "Skriv inn denne nøkkelen på en annen enhet",
|
||||||
|
@ -79,43 +80,43 @@
|
||||||
"close-about_aria-label": "Lukk «Om PairDrop»",
|
"close-about_aria-label": "Lukk «Om PairDrop»",
|
||||||
"faq_title": "Ofte stilte spørsmål",
|
"faq_title": "Ofte stilte spørsmål",
|
||||||
"claim": "Den enkleste måten å overføre filer mellom enheter",
|
"claim": "Den enkleste måten å overføre filer mellom enheter",
|
||||||
"buy-me-a-coffee_title": "Spander drikke.",
|
"buy-me-a-coffee_title": "Spander drikke!",
|
||||||
"tweet_title": "Tvitre om PairDrop",
|
"tweet_title": "Tvitre om PairDrop",
|
||||||
"github_title": "PairDrop på GitHub"
|
"github_title": "PairDrop på GitHub"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"copied-to-clipboard": "Kopiert til utklippstavlen",
|
"copied-to-clipboard": "Kopiert til utklippstavlen",
|
||||||
"pairing-tabs-error": "Sammenkobling av to nettleserfaner er ikke mulig.",
|
"pairing-tabs-error": "Sammenkobling av to nettleserfaner er ikke mulig",
|
||||||
"notifications-enabled": "Merknader påskrudd.",
|
"notifications-enabled": "Merknader påskrudd",
|
||||||
"click-to-show": "Klikk for å vise",
|
"click-to-show": "Klikk for å vise",
|
||||||
"copied-text": "Tekst kopiert til utklippstavlen",
|
"copied-text": "Tekst kopiert til utklippstavlen",
|
||||||
"connected": "Tilkoblet.",
|
"connected": "Tilkoblet",
|
||||||
"online": "Du er tilbake på nett",
|
"online": "Du er tilbake på nett",
|
||||||
"file-transfer-completed": "Filoverføring utført.",
|
"file-transfer-completed": "Filoverføring utført",
|
||||||
"selected-peer-left": "Valgt likemann dro.",
|
"selected-peer-left": "Valgt likemann dro",
|
||||||
"pairing-key-invalid": "Ugyldig nøkkel",
|
"pairing-key-invalid": "Ugyldig nøkkel",
|
||||||
"connecting": "Kobler til …",
|
"connecting": "Kobler til …",
|
||||||
"pairing-not-persistent": "Sammenkoblede enheter er ikke vedvarende.",
|
"pairing-not-persistent": "Sammenkoblede enheter er ikke vedvarende",
|
||||||
"offline": "Du er frakoblet",
|
"offline": "Du er frakoblet",
|
||||||
"online-requirement": "Du må være på nett for å koble sammen enheter.",
|
"online-requirement": "Du må være på nett for å koble sammen enheter.",
|
||||||
"display-name-random-again": "Visningsnavnet er tilfeldig generert igjen.",
|
"display-name-random-again": "Visningsnavnet er tilfeldig generert igjen",
|
||||||
"display-name-changed-permanently": "Visningsnavnet er endret for godt.",
|
"display-name-changed-permanently": "Visningsnavnet er endret for godt",
|
||||||
"display-name-changed-temporarily": "Visningsnavnet er endret kun for denne økten.",
|
"display-name-changed-temporarily": "Visningsnavnet er endret kun for denne økten",
|
||||||
"text-content-incorrect": "Tekstinnholdet er uriktig.",
|
"text-content-incorrect": "Tekstinnholdet er uriktig",
|
||||||
"file-content-incorrect": "Filinnholdet er uriktig.",
|
"file-content-incorrect": "Filinnholdet er uriktig",
|
||||||
"click-to-download": "Klikk for å laste ned",
|
"click-to-download": "Klikk for å laste ned",
|
||||||
"message-transfer-completed": "Meldingsoverføring utført.",
|
"message-transfer-completed": "Meldingsoverføring utført",
|
||||||
"download-successful": "{{descriptor}} nedlastet",
|
"download-successful": "{{descriptor}} nedlastet",
|
||||||
"pairing-success": "Enheter sammenkoblet.",
|
"pairing-success": "Enheter sammenkoblet",
|
||||||
"pairing-cleared": "Sammenkobling av alle enheter opphevet.",
|
"pairing-cleared": "Sammenkobling av alle enheter opphevet",
|
||||||
"pairing-key-invalidated": "Nøkkel {{key}} ugyldiggjort.",
|
"pairing-key-invalidated": "Nøkkel {{key}} ugyldiggjort",
|
||||||
"copied-text-error": "Kunne ikke legge innhold i utklkippstavlen. Kopier manuelt!",
|
"copied-text-error": "Kunne ikke legge innhold i utklkippstavlen. Kopier manuelt!",
|
||||||
"clipboard-content-incorrect": "Utklippstavleinnholdet er uriktig.",
|
"clipboard-content-incorrect": "Utklippstavleinnholdet er uriktig",
|
||||||
"link-received": "Lenke mottatt av {{name}} - Klikk for å åpne.",
|
"link-received": "Lenke mottatt av {{name}} - Klikk for å åpne",
|
||||||
"request-title": "{{name}} ønsker å overføre {{count}} {{descriptor}}",
|
"request-title": "{{name}} ønsker å overføre {{count}} {{descriptor}}",
|
||||||
"message-received": "Melding mottatt av {{name}} - Klikk for å åpne",
|
"message-received": "Melding mottatt av {{name}} - Klikk for å åpne",
|
||||||
"files-incorrect": "Filene er uriktige.",
|
"files-incorrect": "Filene er uriktige",
|
||||||
"ios-memory-limit": "Forsendelse av filer til iOS er kun mulig opptil 200 MB av gangen.",
|
"ios-memory-limit": "Forsendelse av filer til iOS er kun mulig opptil 200 MB av gangen",
|
||||||
"unfinished-transfers-warning": "Lukk med ufullførte overføringer?",
|
"unfinished-transfers-warning": "Lukk med ufullførte overføringer?",
|
||||||
"rate-limit-join-key": "Forsøksgrense overskredet. Vent 10 sek. og prøv igjen."
|
"rate-limit-join-key": "Forsøksgrense overskredet. Vent 10 sek. og prøv igjen."
|
||||||
},
|
},
|
||||||
|
@ -132,7 +133,7 @@
|
||||||
"processing": "Behandler …",
|
"processing": "Behandler …",
|
||||||
"transferring": "Overfører …",
|
"transferring": "Overfører …",
|
||||||
"click-to-send": "Klikk for å sende filer, eller høyreklikk for å sende en melding",
|
"click-to-send": "Klikk for å sende filer, eller høyreklikk for å sende en melding",
|
||||||
"click-to-send-paste-mode": "Klikk for å sende {{descriptor}}",
|
"click-to-send-share-mode": "Klikk for å sende {{descriptor}}",
|
||||||
"connection-hash": "Sammenlign dette sikkerhetsnummeret på begge enhetene for å bekrefte ende-til-ende -krypteringen."
|
"connection-hash": "Sammenlign dette sikkerhetsnummeret på begge enhetene for å bekrefte ende-til-ende -krypteringen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,42 +20,42 @@
|
||||||
"message-received": "Bericht ontvangen van {{name}} - Klik om te kopiëren",
|
"message-received": "Bericht ontvangen van {{name}} - Klik om te kopiëren",
|
||||||
"rate-limit-join-key": "Tempolimiet bereikt. Wacht 10 seconde en probeer opnieuw.",
|
"rate-limit-join-key": "Tempolimiet bereikt. Wacht 10 seconde en probeer opnieuw.",
|
||||||
"connecting": "Verbinden…",
|
"connecting": "Verbinden…",
|
||||||
"pairing-key-invalidated": "Sleutel {{key}} ongeldig.",
|
"pairing-key-invalidated": "Sleutel {{key}} ongeldig",
|
||||||
"pairing-key-invalid": "Ongeldige sleutel",
|
"pairing-key-invalid": "Ongeldige sleutel",
|
||||||
"connected": "Verbonden.",
|
"connected": "Verbonden",
|
||||||
"pairing-not-persistent": "Gekoppelde apparaten zijn niet persistent.",
|
"pairing-not-persistent": "Gekoppelde apparaten zijn niet persistent",
|
||||||
"text-content-incorrect": "Tekst inhoud is incorrect.",
|
"text-content-incorrect": "Tekst inhoud is incorrect",
|
||||||
"message-transfer-completed": "Berichtsoverdracht compleet.",
|
"message-transfer-completed": "Berichtsoverdracht compleet",
|
||||||
"file-transfer-completed": "Bestandsoverdracht compleet.",
|
"file-transfer-completed": "Bestandsoverdracht compleet",
|
||||||
"file-content-incorrect": "Bestandsinhoud is incorrect.",
|
"file-content-incorrect": "Bestandsinhoud is incorrect",
|
||||||
"files-incorrect": "Bestanden zijn incorrect.",
|
"files-incorrect": "Bestanden zijn incorrect",
|
||||||
"selected-peer-left": "Gekozen peer is vertrokken.",
|
"selected-peer-left": "Gekozen peer is vertrokken",
|
||||||
"link-received": "Link van {{name}} ontvangen - Klik om te openen",
|
"link-received": "Link van {{name}} ontvangen - Klik om te openen",
|
||||||
"online": "U bent terug online",
|
"online": "U bent terug online",
|
||||||
"public-room-left": "Openbare ruimte {{publicRoomId}} verlaten",
|
"public-room-left": "Openbare ruimte {{publicRoomId}} verlaten",
|
||||||
"copied-text": "Tekst naar klembord gekopieërd",
|
"copied-text": "Tekst naar klembord gekopieërd",
|
||||||
"display-name-random-again": "De weergavenaam is opnieuw willekeurig gegenereerd.",
|
"display-name-random-again": "De weergavenaam is opnieuw willekeurig gegenereerd",
|
||||||
"display-name-changed-permanently": "De weergavenaam is permanent gewijzigd.",
|
"display-name-changed-permanently": "De weergavenaam is permanent gewijzigd",
|
||||||
"copied-to-clipboard-error": "Kopiëren is niet mogelijk. Kopieer handmatig.",
|
"copied-to-clipboard-error": "Kopiëren is niet mogelijk. Kopieer handmatig.",
|
||||||
"pairing-success": "Apparaten gekoppeld.",
|
"pairing-success": "Apparaten gekoppeld",
|
||||||
"clipboard-content-incorrect": "De inhoud van het klembord is incorrect.",
|
"clipboard-content-incorrect": "De inhoud van het klembord is incorrect",
|
||||||
"display-name-changed-temporarily": "De weergavenaam is alleen voor deze sessie gewijzigd.",
|
"display-name-changed-temporarily": "De weergavenaam is alleen voor deze sessie gewijzigd",
|
||||||
"copied-to-clipboard": "Gekopieerd naar klembord",
|
"copied-to-clipboard": "Gekopieerd naar klembord",
|
||||||
"offline": "U bent offline",
|
"offline": "U bent offline",
|
||||||
"pairing-tabs-error": "Twee webbrowser tabbladen koppelen in is onmogelijk.",
|
"pairing-tabs-error": "Twee webbrowser tabbladen koppelen in is onmogelijk",
|
||||||
"public-room-id-invalid": "Ongeldig kamer ID",
|
"public-room-id-invalid": "Ongeldig kamer ID",
|
||||||
"click-to-download": "Klik om te downloaden",
|
"click-to-download": "Klik om te downloaden",
|
||||||
"pairing-cleared": "Alle apparaten ontkoppeld.",
|
"pairing-cleared": "Alle apparaten ontkoppeld",
|
||||||
"notifications-enabled": "Meldingen geactiveerd.",
|
"notifications-enabled": "Meldingen geactiveerd",
|
||||||
"online-requirement-pairing": "U moet online zijn om apparaten te koppelen.",
|
"online-requirement-pairing": "U moet online zijn om apparaten te koppelen",
|
||||||
"ios-memory-limit": "Bestandsoverdrachten naar iOS kunnen slechts met 200 MB per keer",
|
"ios-memory-limit": "Bestandsoverdrachten naar iOS kunnen slechts met 200 MB per keer",
|
||||||
"online-requirement-public-room": "U moet online zijn om een openbare kamer te maken.",
|
"online-requirement-public-room": "U moet online zijn om een openbare kamer te maken",
|
||||||
"copied-text-error": "Schrijven naar klembord mislukt. Kopieer handmatig!",
|
"copied-text-error": "Schrijven naar klembord mislukt. Kopieer handmatig!",
|
||||||
"download-successful": "{{descriptor}} downloaden",
|
"download-successful": "{{descriptor}} downloaden",
|
||||||
"click-to-show": "Klik om te tonen"
|
"click-to-show": "Klik om te tonen"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"cancel-paste-mode": "Klaar",
|
"cancel-share-mode": "Klaar",
|
||||||
"theme-auto_title": "Gebruik systeemstijl",
|
"theme-auto_title": "Gebruik systeemstijl",
|
||||||
"install_title": "PairDrop installeren",
|
"install_title": "PairDrop installeren",
|
||||||
"theme-dark_title": "Altijd donkere modus gebruiken",
|
"theme-dark_title": "Altijd donkere modus gebruiken",
|
||||||
|
@ -70,12 +70,12 @@
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_mobile": "Tik om bestanden te versturen of houdt vast om een bericht te sturen",
|
"x-instructions_mobile": "Tik om bestanden te versturen of houdt vast om een bericht te sturen",
|
||||||
"click-to-send": "Klik om te verzenden",
|
"x-instructions-share-mode_desktop": "Klik om te verzenden",
|
||||||
"activate-paste-mode-and-other-files": "en {{count}} andere bestanden",
|
"activate-share-mode-and-other-files-plural": "en {{count}} andere bestanden",
|
||||||
"tap-to-send": "Tik om te verzenden",
|
"x-instructions-share-mode_mobile": "Tik om te verzenden",
|
||||||
"activate-paste-mode-base": "Open PairDrop op andere apparaten om te verzenden",
|
"activate-share-mode-base": "Open PairDrop op andere apparaten om te verzenden",
|
||||||
"no-peers-subtitle": "Koppel apparaten of betreed een openbare ruimte om op andere netwerken zichtbaar te worden",
|
"no-peers-subtitle": "Koppel apparaten of betreed een openbare ruimte om op andere netwerken zichtbaar te worden",
|
||||||
"activate-paste-mode-shared-text": "gedeelde tekst",
|
"activate-share-mode-shared-text": "gedeelde tekst",
|
||||||
"x-instructions_desktop": "Klik om bestanden te versturen of rechtsklik om een bericht te sturen",
|
"x-instructions_desktop": "Klik om bestanden te versturen of rechtsklik om een bericht te sturen",
|
||||||
"no-peers-title": "Open PairDrop op andere apparaten om bestanden te versturen",
|
"no-peers-title": "Open PairDrop op andere apparaten om bestanden te versturen",
|
||||||
"x-instructions_data-drop-peer": "Laat los om naar peer te versturen",
|
"x-instructions_data-drop-peer": "Laat los om naar peer te versturen",
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"processing": "Verwerken…",
|
"processing": "Verwerken…",
|
||||||
"click-to-send-paste-mode": "Klik om {{descriptor}} te versturen",
|
"click-to-send-share-mode": "Klik om {{descriptor}} te versturen",
|
||||||
"click-to-send": "Klik om bestanden te versturen of rechtsklik om een bericht te versturen",
|
"click-to-send": "Klik om bestanden te versturen of rechtsklik om een bericht te versturen",
|
||||||
"waiting": "Wachten…",
|
"waiting": "Wachten…",
|
||||||
"connection-hash": "Vergelijk dit veiligheidsnummer op beide apparaten, om de beveiliging van de eind-tot-eind versleuteling te verifiëren",
|
"connection-hash": "Vergelijk dit veiligheidsnummer op beide apparaten, om de beveiliging van de eind-tot-eind versleuteling te verifiëren",
|
||||||
|
|
165
public/lang/pt-BR.json
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"about_title": "Sobre o PairDrop",
|
||||||
|
"language-selector_title": "Definir idioma",
|
||||||
|
"about_aria-label": "Abrir Sobre o PairDrop",
|
||||||
|
"theme-auto_title": "Adaptar o tema ao sistema automaticamente",
|
||||||
|
"theme-light_title": "Sempre usar o tema claro",
|
||||||
|
"theme-dark_title": "Sempre usar o tema escuro",
|
||||||
|
"notification_title": "Ativar notificações",
|
||||||
|
"install_title": "Instalar o PairDrop",
|
||||||
|
"pair-device_title": "Emparelhar seus dispositivos permanentemente",
|
||||||
|
"edit-paired-devices_title": "Editar dispositivos emparelhados",
|
||||||
|
"join-public-room_title": "Entrar em uma sala pública temporariamente",
|
||||||
|
"cancel-share-mode": "Concluído"
|
||||||
|
},
|
||||||
|
"instructions": {
|
||||||
|
"no-peers_data-drop-bg": "Solte para selecionar o destinatário",
|
||||||
|
"no-peers-title": "Abra o PairDrop em outros dispositivos para enviar arquivos",
|
||||||
|
"no-peers-subtitle": "Emparelhe dispositivos ou entre em uma sala pública para ser descoberto em outras redes",
|
||||||
|
"x-instructions_desktop": "Clique para enviar arquivos ou clique com o botão direito para enviar uma mensagem",
|
||||||
|
"x-instructions_mobile": "Toque para enviar arquivos ou toque e segure para enviar uma mensagem",
|
||||||
|
"x-instructions_data-drop-peer": "Solte para enviar para o par",
|
||||||
|
"x-instructions_data-drop-bg": "Solte para selecionar o destinatário",
|
||||||
|
"x-instructions-share-mode_desktop": "Clique para enviar",
|
||||||
|
"x-instructions-share-mode_mobile": "Toque para enviar",
|
||||||
|
"activate-share-mode-base": "Abra o PairDrop em outros dispositivos para enviar",
|
||||||
|
"activate-share-mode-and-other-files-plural": "e {{count}} outros arquivos",
|
||||||
|
"activate-share-mode-shared-text": "texto compartilhado"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"known-as": "Você é conhecido como:",
|
||||||
|
"display-name_data-placeholder": "Carregando…",
|
||||||
|
"display-name_title": "Edite o nome do seu dispositivo permanentemente",
|
||||||
|
"discovery": "Você pode ser descoberto:",
|
||||||
|
"on-this-network": "nesta rede",
|
||||||
|
"on-this-network_title": "Você pode ser descoberto por todos nesta rede.",
|
||||||
|
"paired-devices": "por dispositivos emparelhados",
|
||||||
|
"paired-devices_title": "Você pode ser descoberto por dispositivos emparelhados a qualquer momento, independentemente da rede.",
|
||||||
|
"public-room-devices": "na sala {{roomId}}",
|
||||||
|
"public-room-devices_title": "Você pode ser descoberto por dispositivos nesta sala pública, independentemente da rede.",
|
||||||
|
"traffic": "O tráfego é",
|
||||||
|
"routed": "roteado pelo servidor",
|
||||||
|
"webrtc": "se o WebRTC não estiver disponível."
|
||||||
|
},
|
||||||
|
"dialogs": {
|
||||||
|
"pair-devices-title": "Emparelhar Dispositivos Permanentemente",
|
||||||
|
"input-key-on-this-device": "Insira esta chave em outro dispositivo",
|
||||||
|
"scan-qr-code": "ou escaneie o código QR.",
|
||||||
|
"enter-key-from-another-device": "Insira a chave de outro dispositivo aqui.",
|
||||||
|
"temporary-public-room-title": "Sala Pública Temporária",
|
||||||
|
"input-room-id-on-another-device": "Insira este ID de sala em outro dispositivo",
|
||||||
|
"enter-room-id-from-another-device": "Insira o ID da sala de outro dispositivo para entrar na sala.",
|
||||||
|
"hr-or": "OU",
|
||||||
|
"pair": "Emparelhar",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"edit-paired-devices-title": "Editar Dispositivos Emparelhados",
|
||||||
|
"unpair": "Desemparelhar",
|
||||||
|
"paired-devices-wrapper_data-empty": "Nenhum dispositivo emparelhado.",
|
||||||
|
"auto-accept-instructions-1": "Ative",
|
||||||
|
"auto-accept": "auto-aceitar",
|
||||||
|
"auto-accept-instructions-2": "para aceitar automaticamente todos os arquivos enviados por esse dispositivo.",
|
||||||
|
"close": "Fechar",
|
||||||
|
"join": "Entrar",
|
||||||
|
"leave": "Sair",
|
||||||
|
"would-like-to-share": "gostaria de compartilhar",
|
||||||
|
"accept": "Aceitar",
|
||||||
|
"decline": "Recusar",
|
||||||
|
"has-sent": "enviou:",
|
||||||
|
"share": "Compartilhar",
|
||||||
|
"download": "Baixar",
|
||||||
|
"send-message-title": "Enviar Mensagem",
|
||||||
|
"send-message-to": "Enviar uma Mensagem para",
|
||||||
|
"message_title": "Insira a mensagem a ser enviada",
|
||||||
|
"send": "Enviar",
|
||||||
|
"receive-text-title": "Mensagem Recebida",
|
||||||
|
"copy": "Copiar",
|
||||||
|
"base64-processing": "Processando…",
|
||||||
|
"base64-tap-to-paste": "Toque aqui para colar {{type}}",
|
||||||
|
"base64-paste-to-send": "Cole aqui para enviar {{type}}",
|
||||||
|
"base64-text": "texto",
|
||||||
|
"base64-files": "arquivos",
|
||||||
|
"file-other-description-image": "e mais 1 imagem",
|
||||||
|
"file-other-description-file": "e mais 1 arquivo",
|
||||||
|
"file-other-description-image-plural": "e mais {{count}} imagens",
|
||||||
|
"file-other-description-file-plural": "e mais {{count}} arquivos",
|
||||||
|
"title-image": "Imagem",
|
||||||
|
"title-file": "Arquivo",
|
||||||
|
"title-image-plural": "Imagens",
|
||||||
|
"title-file-plural": "Arquivos",
|
||||||
|
"receive-title": "{{descriptor}} Recebido",
|
||||||
|
"download-again": "Baixar novamente",
|
||||||
|
"language-selector-title": "Definir idioma",
|
||||||
|
"system-language": "Idioma do sistema",
|
||||||
|
"public-room-qr-code_title": "Clique para copiar o link da sala pública",
|
||||||
|
"pair-devices-qr-code_title": "Clique para copiar o link para emparelhar este dispositivo"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"close-about_aria-label": "Fechar Sobre o PairDrop",
|
||||||
|
"claim": "A maneira mais fácil de transferir arquivos entre dispositivos",
|
||||||
|
"github_title": "PairDrop no GitHub",
|
||||||
|
"buy-me-a-coffee_title": "Me compre um café!",
|
||||||
|
"tweet_title": "Tweet sobre o PairDrop",
|
||||||
|
"faq_title": "Perguntas frequentes"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"display-name-changed-permanently": "O nome de exibição é alterado permanentemente",
|
||||||
|
"display-name-changed-temporarily": "O nome de exibição é alterado apenas para esta sessão",
|
||||||
|
"display-name-random-again": "O nome de exibição é gerado aleatoriamente novamente",
|
||||||
|
"download-successful": "{{descriptor}} baixado",
|
||||||
|
"pairing-tabs-error": "Emparelhar duas abas do navegador web é impossível",
|
||||||
|
"pairing-success": "Dispositivos emparelhados",
|
||||||
|
"pairing-not-persistent": "Dispositivos emparelhados não são persistentes",
|
||||||
|
"pairing-key-invalid": "Chave inválida",
|
||||||
|
"pairing-key-invalidated": "Chave {{key}} invalidada",
|
||||||
|
"pairing-cleared": "Todos os dispositivos desemparelhados",
|
||||||
|
"public-room-id-invalid": "ID da sala inválido",
|
||||||
|
"public-room-left": "Saiu da sala pública {{publicRoomId}}",
|
||||||
|
"copied-to-clipboard": "Copiado para a área de transferência",
|
||||||
|
"pair-url-copied-to-clipboard": "Link para emparelhar este dispositivo copiado para a área de transferência",
|
||||||
|
"room-url-copied-to-clipboard": "Link para a sala pública copiado para a área de transferência",
|
||||||
|
"copied-to-clipboard-error": "Cópia não possível. Copie manualmente.",
|
||||||
|
"text-content-incorrect": "O conteúdo do texto está incorreto",
|
||||||
|
"file-content-incorrect": "O conteúdo do arquivo está incorreto",
|
||||||
|
"clipboard-content-incorrect": "O conteúdo da área de transferência está incorreto",
|
||||||
|
"notifications-enabled": "Notificações ativadas",
|
||||||
|
"notifications-permissions-error": "A permissão de notificações foi bloqueada porque o usuário dispensou o prompt de permissão várias vezes. Isso pode ser redefinido nas Informações da Página, que podem ser acessadas clicando no ícone de cadeado ao lado da barra de URL.",
|
||||||
|
"link-received": "Link recebido por {{name}} - Clique para abrir",
|
||||||
|
"message-received": "Mensagem recebida por {{name}} - Clique para copiar",
|
||||||
|
"click-to-download": "Clique para baixar",
|
||||||
|
"request-title": "{{name}} gostaria de transferir {{count}} {{descriptor}}",
|
||||||
|
"click-to-show": "Clique para mostrar",
|
||||||
|
"copied-text": "Texto copiado para a área de transferência",
|
||||||
|
"copied-text-error": "Escrever na área de transferência falhou. Copie manualmente!",
|
||||||
|
"offline": "Você está offline",
|
||||||
|
"online": "Você está online novamente",
|
||||||
|
"connected": "Conectado",
|
||||||
|
"online-requirement-pairing": "Você precisa estar online para emparelhar dispositivos",
|
||||||
|
"online-requirement-public-room": "Você precisa estar online para criar uma sala pública",
|
||||||
|
"connecting": "Conectando…",
|
||||||
|
"files-incorrect": "Os arquivos estão incorretos",
|
||||||
|
"file-transfer-completed": "Transferência de arquivo concluída",
|
||||||
|
"ios-memory-limit": "Enviar arquivos para iOS só é possível até 200 MB de uma vez",
|
||||||
|
"message-transfer-completed": "Transferência de mensagem concluída",
|
||||||
|
"unfinished-transfers-warning": "Há transferências inacabadas. Tem certeza de que deseja fechar o PairDrop?",
|
||||||
|
"rate-limit-join-key": "Limite de taxa atingido. Aguarde 10 segundos e tente novamente.",
|
||||||
|
"selected-peer-left": "Par selecionado saiu"
|
||||||
|
},
|
||||||
|
"document-titles": {
|
||||||
|
"file-received": "Arquivo recebido",
|
||||||
|
"file-received-plural": "{{count}} Arquivos recebidos",
|
||||||
|
"file-transfer-requested": "Transferência de arquivo solicitada",
|
||||||
|
"image-transfer-requested": "Transferência de imagem solicitada",
|
||||||
|
"message-received": "Mensagem recebida",
|
||||||
|
"message-received-plural": "{{count}} mensagens recebidas"
|
||||||
|
},
|
||||||
|
"peer-ui": {
|
||||||
|
"click-to-send-share-mode": "Clique para enviar {{descriptor}}",
|
||||||
|
"click-to-send": "Clique para enviar arquivos ou clique com o botão direito para enviar uma mensagem",
|
||||||
|
"connection-hash": "Para verificar a segurança da criptografia de ponta a ponta, compare este número de segurança em ambos os dispositivos",
|
||||||
|
"preparing": "Preparando…",
|
||||||
|
"waiting": "Aguardando…",
|
||||||
|
"processing": "Processando…",
|
||||||
|
"transferring": "Transferindo…"
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,36 +20,36 @@
|
||||||
"message-received": "Mesaj primit de {{name}} - Apasă pentru a copia",
|
"message-received": "Mesaj primit de {{name}} - Apasă pentru a copia",
|
||||||
"rate-limit-join-key": "A fost atinsă limita ratei. Așteptați 10 secunde și încercați din nou.",
|
"rate-limit-join-key": "A fost atinsă limita ratei. Așteptați 10 secunde și încercați din nou.",
|
||||||
"connecting": "Conectarea…",
|
"connecting": "Conectarea…",
|
||||||
"pairing-key-invalidated": "Cheia {{key}} invalidată.",
|
"pairing-key-invalidated": "Cheia {{key}} invalidată",
|
||||||
"pairing-key-invalid": "Cheie invalidă",
|
"pairing-key-invalid": "Cheie invalidă",
|
||||||
"connected": "Conectat.",
|
"connected": "Conectat",
|
||||||
"pairing-not-persistent": "Dispozitivele cuplate nu sunt persistente.",
|
"pairing-not-persistent": "Dispozitivele cuplate nu sunt persistente",
|
||||||
"text-content-incorrect": "Conținutul textului este incorect.",
|
"text-content-incorrect": "Conținutul textului este incorect",
|
||||||
"message-transfer-completed": "Transferul mesajului este finalizat.",
|
"message-transfer-completed": "Transferul mesajului este finalizat",
|
||||||
"file-transfer-completed": "Transfer de fișiere finalizat.",
|
"file-transfer-completed": "Transfer de fișiere finalizat",
|
||||||
"file-content-incorrect": "Conținutul fișierului este incorect.",
|
"file-content-incorrect": "Conținutul fișierului este incorect",
|
||||||
"files-incorrect": "Fișierele sunt incorecte.",
|
"files-incorrect": "Fișierele sunt incorecte",
|
||||||
"selected-peer-left": "Selectat peer a plecat.",
|
"selected-peer-left": "Selectat peer a plecat",
|
||||||
"link-received": "Link primit de {{name}} - Apasă pentru a deschide",
|
"link-received": "Link primit de {{name}} - Apasă pentru a deschide",
|
||||||
"online": "Ați revenit online",
|
"online": "Ați revenit online",
|
||||||
"public-room-left": "Plecat din camera publică {{publicRoomId}}",
|
"public-room-left": "Plecat din camera publică {{publicRoomId}}",
|
||||||
"copied-text": "Text copiat în clipboard",
|
"copied-text": "Text copiat în clipboard",
|
||||||
"display-name-random-again": "Numele afișat este din nou generat aleatoriu.",
|
"display-name-random-again": "Numele afișat este din nou generat aleatoriu",
|
||||||
"display-name-changed-permanently": "Numele afișat este schimbat permanent.",
|
"display-name-changed-permanently": "Numele afișat este schimbat permanent",
|
||||||
"copied-to-clipboard-error": "Copierea nu este posibilă. Copiați manual.",
|
"copied-to-clipboard-error": "Copierea nu este posibilă. Copiați manual.",
|
||||||
"pairing-success": "Dispozitive asociate.",
|
"pairing-success": "Dispozitive asociate",
|
||||||
"clipboard-content-incorrect": "Conținutul clipboard-ului este incorect.",
|
"clipboard-content-incorrect": "Conținutul clipboard-ului este incorect",
|
||||||
"display-name-changed-temporarily": "Numele afișat se modifică numai pentru această sesiune.",
|
"display-name-changed-temporarily": "Numele afișat se modifică numai pentru această sesiune",
|
||||||
"copied-to-clipboard": "Copiat în clipboard",
|
"copied-to-clipboard": "Copiat în clipboard",
|
||||||
"offline": "Ești offline",
|
"offline": "Ești offline",
|
||||||
"pairing-tabs-error": "Cuplarea între două file de browser web este imposibilă.",
|
"pairing-tabs-error": "Cuplarea între două file de browser web este imposibilă",
|
||||||
"public-room-id-invalid": "ID-ul camerei invalid",
|
"public-room-id-invalid": "ID-ul camerei invalid",
|
||||||
"click-to-download": "Apasă pentru a descărca",
|
"click-to-download": "Apasă pentru a descărca",
|
||||||
"pairing-cleared": "Toate dispozitivele sunt decuplate.",
|
"pairing-cleared": "Toate dispozitivele sunt decuplate",
|
||||||
"notifications-enabled": "Notificări activate.",
|
"notifications-enabled": "Notificări activate",
|
||||||
"online-requirement-pairing": "Trebuie să fiți online pentru a asocia dispozitivele.",
|
"online-requirement-pairing": "Trebuie să fiți online pentru a asocia dispozitivele",
|
||||||
"ios-memory-limit": "Trimiterea de fișiere pe iOS este posibilă doar până la 200 MB simultan",
|
"ios-memory-limit": "Trimiterea de fișiere pe iOS este posibilă doar până la 200 MB simultan",
|
||||||
"online-requirement-public-room": "Trebuie să fiți online pentru a crea o cameră publică.",
|
"online-requirement-public-room": "Trebuie să fiți online pentru a crea o cameră publică",
|
||||||
"copied-text-error": "Scrierea în clipboard a eșuat. Copiați manual!",
|
"copied-text-error": "Scrierea în clipboard a eșuat. Copiați manual!",
|
||||||
"download-successful": "{{descriptor}} descărcat",
|
"download-successful": "{{descriptor}} descărcat",
|
||||||
"click-to-show": "Apasă pentru a arăta",
|
"click-to-show": "Apasă pentru a arăta",
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
"room-url-copied-to-clipboard": "Link către sala publică copiat în clipboard"
|
"room-url-copied-to-clipboard": "Link către sala publică copiat în clipboard"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"cancel-paste-mode": "Gata",
|
"cancel-share-mode": "Gata",
|
||||||
"theme-auto_title": "Adaptează tema la sistem",
|
"theme-auto_title": "Adaptează tema la sistem",
|
||||||
"install_title": "Instalează PairDrop",
|
"install_title": "Instalează PairDrop",
|
||||||
"theme-dark_title": "Utilizați mereu tema întunecoasă",
|
"theme-dark_title": "Utilizați mereu tema întunecoasă",
|
||||||
|
@ -73,12 +73,12 @@
|
||||||
},
|
},
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_mobile": "Atingeți pentru a trimite fișiere sau atingeți lung pentru a trimite un mesaj",
|
"x-instructions_mobile": "Atingeți pentru a trimite fișiere sau atingeți lung pentru a trimite un mesaj",
|
||||||
"click-to-send": "Clic pentru a trimite",
|
"x-instructions-share-mode_desktop": "Clic pentru a trimite",
|
||||||
"activate-paste-mode-and-other-files": "și {{count}} alte fișiere",
|
"activate-share-mode-and-other-files-plural": "și {{count}} alte fișiere",
|
||||||
"tap-to-send": "Atinge pentru a trimite",
|
"x-instructions-share-mode_mobile": "Atinge pentru a trimite",
|
||||||
"activate-paste-mode-base": "Deschideți PairDrop pe alte dispozitive pentru a trimite",
|
"activate-share-mode-base": "Deschideți PairDrop pe alte dispozitive pentru a trimite",
|
||||||
"no-peers-subtitle": "Împerecheați dispozitive sau intrați într-o cameră publică pentru a fi descoperit în alte rețele",
|
"no-peers-subtitle": "Împerecheați dispozitive sau intrați într-o cameră publică pentru a fi descoperit în alte rețele",
|
||||||
"activate-paste-mode-shared-text": "text partajat",
|
"activate-share-mode-shared-text": "text partajat",
|
||||||
"x-instructions_desktop": "Dați clic pentru a trimite fișiere sau dați clic dreapta pentru a trimite un mesaj",
|
"x-instructions_desktop": "Dați clic pentru a trimite fișiere sau dați clic dreapta pentru a trimite un mesaj",
|
||||||
"no-peers-title": "Deschideți PairDrop pe alte dispozitive pentru a trimite fișiere",
|
"no-peers-title": "Deschideți PairDrop pe alte dispozitive pentru a trimite fișiere",
|
||||||
"x-instructions_data-drop-peer": "Eliberare pentru a trimite la peer",
|
"x-instructions_data-drop-peer": "Eliberare pentru a trimite la peer",
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"processing": "Procesarea…",
|
"processing": "Procesarea…",
|
||||||
"click-to-send-paste-mode": "Apasă pentru a trimite {{descriptor}}",
|
"click-to-send-share-mode": "Apasă pentru a trimite {{descriptor}}",
|
||||||
"click-to-send": "Apasă pentru a trimite fișiere sau apasă cu butonul din dreapta pentru a trimite un mesaj",
|
"click-to-send": "Apasă pentru a trimite fișiere sau apasă cu butonul din dreapta pentru a trimite un mesaj",
|
||||||
"waiting": "Așteptând…",
|
"waiting": "Așteptând…",
|
||||||
"connection-hash": "Pentru a verifica securitatea criptării end-to-end, comparați acest număr de securitate pe ambele dispozitive",
|
"connection-hash": "Pentru a verifica securitatea criptării end-to-end, comparați acest număr de securitate pe ambele dispozitive",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"about_aria-label": "Открыть страницу \"О сервисе\"",
|
"about_aria-label": "Открыть страницу \"О сервисе\"",
|
||||||
"pair-device_title": "Связать ваши устройства навсегда",
|
"pair-device_title": "Связать ваши устройства навсегда",
|
||||||
"install_title": "Установить PairDrop",
|
"install_title": "Установить PairDrop",
|
||||||
"cancel-paste-mode": "Выполнено",
|
"cancel-share-mode": "Выполнено",
|
||||||
"edit-paired-devices_title": "Редактировать связанные устройства",
|
"edit-paired-devices_title": "Редактировать связанные устройства",
|
||||||
"notification_title": "Включить уведомления",
|
"notification_title": "Включить уведомления",
|
||||||
"about_title": "О сервисе",
|
"about_title": "О сервисе",
|
||||||
|
@ -16,16 +16,16 @@
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"x-instructions_desktop": "Нажмите, чтобы отправить файлы, или щелкните правой кнопкой мыши, чтобы отправить сообщение",
|
"x-instructions_desktop": "Нажмите, чтобы отправить файлы, или щелкните правой кнопкой мыши, чтобы отправить сообщение",
|
||||||
"no-peers_data-drop-bg": "Отпустите, чтобы выбрать получателя",
|
"no-peers_data-drop-bg": "Отпустите, чтобы выбрать получателя",
|
||||||
"click-to-send": "Нажмите, чтобы отправить",
|
"x-instructions-share-mode_desktop": "Нажмите, чтобы отправить",
|
||||||
"x-instructions_data-drop-bg": "Отпустите, чтобы выбрать получателя",
|
"x-instructions_data-drop-bg": "Отпустите, чтобы выбрать получателя",
|
||||||
"tap-to-send": "Прикоснитесь, чтобы отправить",
|
"x-instructions-share-mode_mobile": "Прикоснитесь, чтобы отправить",
|
||||||
"x-instructions_data-drop-peer": "Отпустите, чтобы послать узлу",
|
"x-instructions_data-drop-peer": "Отпустите, чтобы послать узлу",
|
||||||
"x-instructions_mobile": "Прикоснитесь коротко, чтобы отправить файлы, или долго, чтобы отправить сообщение",
|
"x-instructions_mobile": "Прикоснитесь коротко, чтобы отправить файлы, или долго, чтобы отправить сообщение",
|
||||||
"no-peers-title": "Откройте PairDrop на других устройствах, чтобы отправить файлы",
|
"no-peers-title": "Откройте PairDrop на других устройствах, чтобы отправить файлы",
|
||||||
"no-peers-subtitle": "Свяжите устройства или войдите в публичную комнату, чтобы вас могли обнаружить из других сетей",
|
"no-peers-subtitle": "Свяжите устройства или войдите в публичную комнату, чтобы вас могли обнаружить из других сетей",
|
||||||
"activate-paste-mode-and-other-files": "и {{count}} других файлов",
|
"activate-share-mode-and-other-files-plural": "и {{count}} других файлов",
|
||||||
"activate-paste-mode-base": "Откройте PairDrop на других устройствах, чтобы отправить",
|
"activate-share-mode-base": "Откройте PairDrop на других устройствах, чтобы отправить",
|
||||||
"activate-paste-mode-shared-text": "общий текст"
|
"activate-share-mode-shared-text": "общий текст"
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"display-name_data-placeholder": "Загрузка…",
|
"display-name_data-placeholder": "Загрузка…",
|
||||||
|
@ -104,51 +104,51 @@
|
||||||
"faq_title": "Часто задаваемые вопросы"
|
"faq_title": "Часто задаваемые вопросы"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"display-name-changed-permanently": "Отображаемое имя было изменено навсегда.",
|
"display-name-changed-permanently": "Отображаемое имя было изменено навсегда",
|
||||||
"display-name-random-again": "Отображаемое имя сгенерировалось случайным образом снова.",
|
"display-name-random-again": "Отображаемое имя сгенерировалось случайным образом снова",
|
||||||
"pairing-success": "Устройства связаны.",
|
"pairing-success": "Устройства связаны",
|
||||||
"pairing-tabs-error": "Связка двух вкладок браузера невозможна.",
|
"pairing-tabs-error": "Связка двух вкладок браузера невозможна",
|
||||||
"copied-to-clipboard": "Скопировано в буфер обмена",
|
"copied-to-clipboard": "Скопировано в буфер обмена",
|
||||||
"pairing-not-persistent": "Связанные устройства непостоянны.",
|
"pairing-not-persistent": "Связанные устройства непостоянны",
|
||||||
"link-received": "Получена ссылка от {{name}} - нажмите, чтобы открыть",
|
"link-received": "Получена ссылка от {{name}} - нажмите, чтобы открыть",
|
||||||
"notifications-enabled": "Уведомления включены.",
|
"notifications-enabled": "Уведомления включены",
|
||||||
"text-content-incorrect": "Содержание текста неверно.",
|
"text-content-incorrect": "Содержание текста неверно",
|
||||||
"message-received": "Получено сообщение от {{name}} - нажмите, чтобы скопировать",
|
"message-received": "Получено сообщение от {{name}} - нажмите, чтобы скопировать",
|
||||||
"connected": "Подключено.",
|
"connected": "Подключено",
|
||||||
"copied-text": "Текст скопирован в буфер обмена",
|
"copied-text": "Текст скопирован в буфер обмена",
|
||||||
"online": "Вы снова в сети",
|
"online": "Вы снова в сети",
|
||||||
"offline": "Вы находитесь вне сети",
|
"offline": "Вы находитесь вне сети",
|
||||||
"online-requirement": "Для сопряжения устройств вам нужно быть в сети.",
|
"online-requirement": "Для сопряжения устройств вам нужно быть в сети.",
|
||||||
"files-incorrect": "Файлы неверны.",
|
"files-incorrect": "Файлы неверны",
|
||||||
"message-transfer-completed": "Передача сообщения завершена.",
|
"message-transfer-completed": "Передача сообщения завершена",
|
||||||
"ios-memory-limit": "Отправка файлов на iOS устройства возможна только до 200 МБ за один раз",
|
"ios-memory-limit": "Отправка файлов на iOS устройства возможна только до 200 МБ за один раз",
|
||||||
"selected-peer-left": "Выбранный узел вышел.",
|
"selected-peer-left": "Выбранный узел вышел",
|
||||||
"request-title": "{{name}} хотел бы передать {{count}} {{descriptor}}",
|
"request-title": "{{name}} хотел бы передать {{count}} {{descriptor}}",
|
||||||
"rate-limit-join-key": "Достигнут предел скорости. Подождите 10 секунд и повторите попытку.",
|
"rate-limit-join-key": "Достигнут предел скорости. Подождите 10 секунд и повторите попытку.",
|
||||||
"unfinished-transfers-warning": "Есть незавершенные передачи. Вы уверены, что хотите закрыть PairDrop?",
|
"unfinished-transfers-warning": "Есть незавершенные передачи. Вы уверены, что хотите закрыть PairDrop?",
|
||||||
"copied-text-error": "Запись в буфер обмена не удалась. Скопируйте вручную!",
|
"copied-text-error": "Запись в буфер обмена не удалась. Скопируйте вручную!",
|
||||||
"pairing-cleared": "Все устройства отвязаны.",
|
"pairing-cleared": "Все устройства отвязаны",
|
||||||
"pairing-key-invalid": "Неверный ключ",
|
"pairing-key-invalid": "Неверный ключ",
|
||||||
"pairing-key-invalidated": "Ключ {{key}} признан недействительным.",
|
"pairing-key-invalidated": "Ключ {{key}} признан недействительным",
|
||||||
"click-to-download": "Нажмите, чтобы скачать",
|
"click-to-download": "Нажмите, чтобы скачать",
|
||||||
"clipboard-content-incorrect": "Содержание буфера обмена неверно.",
|
"clipboard-content-incorrect": "Содержание буфера обмена неверно",
|
||||||
"click-to-show": "Нажмите, чтобы показать",
|
"click-to-show": "Нажмите, чтобы показать",
|
||||||
"connecting": "Подключение…",
|
"connecting": "Подключение…",
|
||||||
"download-successful": "{{descriptor}} загружен",
|
"download-successful": "{{descriptor}} загружен",
|
||||||
"display-name-changed-temporarily": "Отображаемое имя было изменено только для этой сессии.",
|
"display-name-changed-temporarily": "Отображаемое имя было изменено только для этой сессии",
|
||||||
"file-content-incorrect": "Содержимое файла неверно.",
|
"file-content-incorrect": "Содержимое файла неверно",
|
||||||
"file-transfer-completed": "Передача файла завершена.",
|
"file-transfer-completed": "Передача файла завершена",
|
||||||
"public-room-left": "Покинуть публичную комнату {{publicRoomId}}",
|
"public-room-left": "Покинуть публичную комнату {{publicRoomId}}",
|
||||||
"copied-to-clipboard-error": "Копирование невозможно. Скопируйте вручную.",
|
"copied-to-clipboard-error": "Копирование невозможно. Скопируйте вручную.",
|
||||||
"public-room-id-invalid": "Неверный ID комнаты",
|
"public-room-id-invalid": "Неверный ID комнаты",
|
||||||
"online-requirement-pairing": "Для связки устройств необходимо находиться быть онлайн.",
|
"online-requirement-pairing": "Для связки устройств необходимо находиться быть онлайн",
|
||||||
"online-requirement-public-room": "Для создания публичной комнаты необходимо быть онлайн.",
|
"online-requirement-public-room": "Для создания публичной комнаты необходимо быть онлайн",
|
||||||
"notifications-permissions-error": "Уведомления были заблокированы, так как пользователь отклонил запрос на их работу несколько раз. Это можно изменить в меню \"О странице\", которое может быть вызвано нажатием на иконку замочка рядом со строкой адреса сайта.",
|
"notifications-permissions-error": "Уведомления были заблокированы, так как пользователь отклонил запрос на их работу несколько раз. Это можно изменить в меню \"О странице\", которое может быть вызвано нажатием на иконку замочка рядом со строкой адреса сайта.",
|
||||||
"pair-url-copied-to-clipboard": "Ссылка для привязки этого устройства была скопирована в буфер обмена",
|
"pair-url-copied-to-clipboard": "Ссылка для привязки этого устройства была скопирована в буфер обмена",
|
||||||
"room-url-copied-to-clipboard": "Ссылка на публичную комнату была скопирована в буфер обмена"
|
"room-url-copied-to-clipboard": "Ссылка на публичную комнату была скопирована в буфер обмена"
|
||||||
},
|
},
|
||||||
"peer-ui": {
|
"peer-ui": {
|
||||||
"click-to-send-paste-mode": "Нажмите, чтобы отправить {{descriptor}}",
|
"click-to-send-share-mode": "Нажмите, чтобы отправить {{descriptor}}",
|
||||||
"preparing": "Подготовка…",
|
"preparing": "Подготовка…",
|
||||||
"transferring": "Передача…",
|
"transferring": "Передача…",
|
||||||
"processing": "Обработка…",
|
"processing": "Обработка…",
|
||||||
|
|